【问题标题】:confused about setState inside onTap Flutter对 onTap Flutter 中的 setState 感到困惑
【发布时间】:2020-11-14 13:03:37
【问题描述】:

我一直在努力学习 Flutter,这部分让我很困惑。感谢一百万您的帮助。 我想将数据(一个对象)从第二个屏幕返回到第一个屏幕。由于我从互联网上阅读但尚未完全理解的帖子,我确实成功了。具体来说:为什么我需要将 categoryData 分配给返回的对象,然后再将其分配给 categoryCard?为什么我不能通过将 categoryCard 分配给返回的 Object 来直接做到这一点?这是有效的代码:

第一屏:

SizedBox(
  height: 95,
  child: GestureDetector(
      onTap: () async {
        final categoryData =
            await Navigator.pushNamed(context, '/EditCategory');
        setState(() => categoryCard = categoryData);
      },
      child: categoryCard),
),

第二屏:

return Card(
  child: ListTile(
    title: Text('${myText[index]}'),
    leading: myIcon[index],
    trailing: Icon(Icons.arrow_forward_ios),
    onTap: () {
      Navigator.pop(
          context,
          CategoryCard(
              categoryIcon: myIcon[index],
              categoryText: Text(
                '${myText[index]}',
                style: TextStyle(fontSize: 20),
              )));
    },
  ),
);

我认为应该可以工作但不行的代码

第一屏:

SizedBox(
  height: 95,
  child: GestureDetector(
      onTap: () {
        setState(() async {
          CategoryCard categoryCard =
              await Navigator.pushNamed(context, '/EditCategory');
        });
      },
      child: categoryCard),
),

第二屏也一样

如果您想知道,这是第一个和第二个屏幕的完整代码:

第一:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'MyApp.dart';

class Input extends StatefulWidget {
  @override
  _InputState createState() => _InputState();
}

class _InputState extends State<Input> {
  CategoryCard categoryCard = CategoryCard();
  final _formKey = GlobalKey<FormState>();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color.fromRGBO(210, 234, 251, 1),
      appBar: appBarInEx(),
      body: TabBarView(
        children: [
          ListView(
            children: [
              Form(
                key: _formKey,
                child: Column(
                  children: [
                    TextFormField(
                      validator: (value) {
                        if (value.isEmpty) {
                          return 'Please enter the value';
                        }
                        return null;
                      },
                    ),
                    Padding(
                      padding: const EdgeInsets.symmetric(vertical: 16.0),
                      child: ElevatedButton(
                        onPressed: () {
                          if (_formKey.currentState.validate()) {
                            Scaffold.of(context).showSnackBar(
                                SnackBar(content: Text('Processing Data')));
                          }
                        },
                        child: Text('Submit'),
                      ),
                    ),
                  ],
                ),
              ),
              SizedBox(
                height: 150,
                child: Card(
                  color: Color.fromRGBO(254, 228, 194, 1),
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.end,
                      children: [
                        SizedBox(
                          height: 20,
                        ),
                        Text(
                          'Amount',
                          style: TextStyle(fontSize: 30),
                        ),
                        SizedBox(
                          height: 15,
                        ),
                        Text(r'0$',
                            style: TextStyle(
                              fontSize: 30,
                            )),
                        Text('_____________________________________',
                            style: TextStyle(
                              fontSize: 15,
                            ))
                      ],
                    ),
                  ),
                ),
              ),
              SizedBox(
                height: 95,
                child: GestureDetector(
                    onTap: () async {
                      final categoryData =
                          await Navigator.pushNamed(context, '/EditCategory');
                      setState(() => categoryCard = categoryData);
                    },
                    child: categoryCard),
              ),
              SizedBox(
                height: 95,
                child: Card(
                  color: Color.fromRGBO(254, 228, 194, 1),
                  child: ListTile(
                    contentPadding:
                        EdgeInsets.symmetric(vertical: 18, horizontal: 16),
                    title: Text(
                      'Note',
                      style: TextStyle(fontSize: 20),
                    ),
                    leading: Icon(Icons.description_outlined, size: 40),
                    trailing: Icon(
                      Icons.arrow_forward_ios_outlined,
                      size: 30,
                    ),
                  ),
                ),
              ),
              SizedBox(
                height: 95,
                child: Card(
                  color: Color.fromRGBO(254, 228, 194, 1),
                  child: ListTile(
                    contentPadding:
                        EdgeInsets.symmetric(vertical: 18, horizontal: 16),
                    title: Text(
                      'Date',
                      style: TextStyle(fontSize: 20),
                    ),
                    leading: Icon(Icons.event, size: 40),
                    trailing: Icon(
                      Icons.arrow_forward_ios_outlined,
                      size: 30,
                    ),
                  ),
                ),
              ),
              Container(
                margin: EdgeInsets.fromLTRB(30, 70, 30, 0),
                child: Padding(
                  padding: const EdgeInsets.all(18.0),
                  child: RaisedButton(
                    shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(18.0),
                        side: BorderSide(color: Colors.red)),
                    elevation: 10,
                    color: Colors.lime,
                    onPressed: () {
                      Navigator.pushNamed(context, '/');
                    },
                    textColor: Colors.white,
                    child: Text(
                      'Save',
                      style: TextStyle(fontSize: 40),
                    ),
                  ),
                ),
              )
            ],
          ),
          Container()
        ],
      ),
    );
  }
}

class CategoryCard extends StatelessWidget {
  final Text categoryText;
  final Icon categoryIcon;

  CategoryCard(
      {Key key,
      this.categoryText = const Text(
        'Category',
        style: TextStyle(fontSize: 20),
      ),
      this.categoryIcon = const Icon(
        Icons.category_outlined,
        size: 40,
      )})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      color: Color.fromRGBO(254, 228, 194, 1),
      child: ListTile(
        contentPadding: EdgeInsets.symmetric(vertical: 18, horizontal: 16),
        title: categoryText,
        leading: categoryIcon,
        trailing: Icon(
          Icons.arrow_forward_ios_outlined,
          size: 30,
        ),
      ),
    );
  }
}

第二:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:material_floating_search_bar/material_floating_search_bar.dart';

import 'Input.dart';
import 'MyApp.dart';

class EditCategory extends StatefulWidget {
  @override
  _EditCategoryState createState() => _EditCategoryState();
}

class _EditCategoryState extends State<EditCategory> {
  List<String> myText = [
    'Add Category',
    'Cosmetic',
    'Education',
    'Clothes',
    'Food',
    'Cosmetic',
    'Education',
    'Clothes',
    'Food'
  ];
  List<Widget> myIcon = [
    Icon(Icons.add_circle_outline),
    Icon(Icons.no_food_outlined),
    Icon(Icons.face),
    Icon(Icons.book_outlined),
    Icon(Icons.g_translate),
    Icon(Icons.no_food_outlined),
    Icon(Icons.face),
    Icon(Icons.book_outlined),
    Icon(Icons.g_translate),
  ];
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        backgroundColor: Color.fromRGBO(210, 234, 251, 1),
        appBar: appBarInEx(),
        body: TabBarView(
          children: [
            Column(
              children: [
                FloatingSearchAppBar(
                  title: const Text('Enter Category Name'),
                  transitionDuration: const Duration(milliseconds: 800),
                  color: Colors.orangeAccent.shade100,
                  colorOnScroll: Colors.greenAccent.shade200,
                  height: 55,
                ),
                Expanded(
                    child: ListView.builder(
                  itemCount: myText.length,
                  itemBuilder: (context, index) {
                    if (index == 0) {
                      return SizedBox(
                          height: 90,
                          child: Card(
                            child: ListTile(
                              title: Text('${myText[index]}'),
                              leading: myIcon[index],
                              trailing: Icon(Icons.arrow_forward_ios),
                            ),
                          ));
                    }
                    return Card(
                      child: ListTile(
                        title: Text('${myText[index]}'),
                        leading: myIcon[index],
                        trailing: Icon(Icons.arrow_forward_ios),
                        onTap: () {
                          Navigator.pop(
                              context,
                              CategoryCard(
                                  categoryIcon: myIcon[index],
                                  categoryText: Text(
                                    '${myText[index]}',
                                    style: TextStyle(fontSize: 20),
                                  )));
                        },
                      ),
                    );
                  },
                )),
              ],
            ),
            Container()
          ],
        ),
      ),
    );
  }
}

【问题讨论】:

  • 你能给我们展示第二个屏幕吗?
  • 你想直接把数据传到第二个Page,对吧?
  • 不,我实际上想将数据从第二个屏幕返回到第一个屏幕(弹出后)。我刚刚编辑了显示 2 个屏幕的所有代码的帖子@thomas.s

标签: flutter dart setstate gesturedetector


【解决方案1】:

好的代码等待onTap 并将结果赋值给一个局部变量。然后它使用setState 将局部变量分配给实例字段。

为什么我需要将 categoryData 分配给返回的对象,然后再将其分配给 categoryCard?为什么不能直接给返回的Object赋值categoryCard呢?

实际上你可以这样做,这会起作用:

GestureDetector(
  onTap: () async {
    categoryData = await Navigator.push(...);
    setState(() {});
  },
),

基本上setState 告诉 Flutter 重建你的小部件,在回调中执行状态设置并不是严格要求。

关于错误代码:您将 setState 与异步回调一起使用,因此 Flutter 会立即重建您的小部件,它不会等待您的异步操作完成。在重建时,categoryData 具有旧值,因此屏幕上没有任何变化。当用户在第二个屏幕上触发onTap 并弹回第一个屏幕时,categoryData 会更新为新值,但您的小部件并未重建,因此它仍然显示过时的数据。

【讨论】:

  • 哦,我重读了有关 setState 的文档,意识到它的回调不能是异步的。您推荐的代码完全有道理,但它仍然无法正常工作(点击时无法推送到第二个屏幕)
猜你喜欢
  • 2023-03-17
  • 1970-01-01
  • 2021-08-16
  • 2021-09-19
  • 1970-01-01
  • 2019-09-13
  • 2012-07-22
  • 2013-05-13
  • 2020-04-16
相关资源
最近更新 更多