【问题标题】:Flutter: Saving the state of a page even after navigating between screensFlutter:即使在屏幕之间导航后也保存页面的状态
【发布时间】:2021-02-26 17:34:42
【问题描述】:

我有一个简单的事情要实现。我的应用程序中有 2 个屏幕,在主屏幕上我有一个按钮,可以将我导航到一个名为 Interests 的新页面。兴趣页面是一个复选框列表(我必须只使用 listview.builder)和一个提交数据的按钮(它将我导航回主屏幕)。我想要实现的是:

  1. 复选框应该可以正常工作。
  2. 当我从兴趣页面导航到主页并再次导航回兴趣页面时,选中的复选框应保持选中状态。简而言之,应该保存页面的状态。
  3. 我写了一个函数“applyInterestChanges”来保存数据库中的数据。我必须检索相同的数据以显示选定的复选框(我们通过构造函数传递数据来执行此操作)。

任何帮助将不胜感激!

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Center(
          child: RaisedButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => Interests(),
                ),
              );
            },
            child: Text("Click here!!"),
          ),
        ),
      ),
    );
  }
}

class Interests extends StatefulWidget {
  final List<dynamic> selectedList;
  final void Function(List<dynamic>) callback;

  Interests(this.selectedList, this.callback);

  @override
  _InterestsState createState() => _InterestsState();
}

class _InterestsState extends State<Interests> {
  Map<String, dynamic> _categories = {
    "responseCode": "1",
    "responseText": "List categories.",
    "responseBody": [
      {"category_id": "1", "category_name": "Movies"},
      {"category_id": "2", "category_name": "Sports"},
      {"category_id": "3", "category_name": "Food"},
      {"category_id": "4", "category_name": "Music"},
      {"category_id": "5", "category_name": "Others"},
    ],
    "responseTotalResult": 5
  };

  void _onCategorySelected(bool selected, categoryName) {
    if (selected == true) {
      setState(() {
        widget.selectedList.add(categoryName);
      });
    } else {
      setState(() {
        widget.selectedList.remove(categoryName);
      });
    }
    widget.callback(widget.selectedList);
  }

  applyInterestChanges() { //function to save the changes in database.
    Firestore.instance
        .collection('my_users')
        .document(currentUserModel.id)
        .updateData({
      "interests": widget.selectedList,
    });
  } //this code is working properly. Need to similar function to retrieve the data and display the updated interests list.

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Interests"),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Text(
              "Select your interests: ",
              style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
            ),
            ListView.builder(
              physics: NeverScrollableScrollPhysics(),
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              itemCount: _categories['responseTotalResult'],
              itemBuilder: (BuildContext context, int index) {
                return CheckboxListTile(
                  controlAffinity: ListTileControlAffinity.leading,
                  value: widget.selectedList.contains(
                      _categories['responseBody'][index]['category_name']),
                  onChanged: (bool selected) {
                    _onCategorySelected(selected,
                        _categories['responseBody'][index]['category_name']);
                  },
                  title:
                      Text(_categories['responseBody'][index]['category_name']),
                );
              },
            ),
            MaterialButton(
              onPressed: () {
                Navigator.pop(context);
                applyInterestChanges();
              },
              child: Text("Submit"),
            ),
          ],
        ),
      ),
    );
  }
}

【问题讨论】:

    标签: firebase flutter google-cloud-firestore navigation state-management


    【解决方案1】:

    您可以从父小部件 MyHomeWidget 传递一个空列表,并通过来自 Interests 小部件的回调更新此列表。

    下次,每当您返回并再次导航到 Interests 小部件时,我们将传递此更新的列表,该列表保存 Interests 小部件的状态。因此,复选框将根据它们在列表中的值进行检查。

    这里是实现:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: MyHomePage(),
          debugShowCheckedModeBanner: false,
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      List<dynamic> selectedList = [];
      
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            child: Center(
              child: RaisedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => Interests(
                        selectedList,
                        (List<dynamic> updatedList) {
                          setState(() {
                            selectedList = updatedList;
                          });
                        }
                      ),
                    ),
                  );
                },
                child: Text("Click here!!"),
              ),
            ),
          ),
        );
      }
    }
    
    class Interests extends StatefulWidget {
      Interests(this.selectedList, this.callback);
      
      // Passing the list from parent widget i.e, MyHomeWidget
      // Initially the list will be empty
      // We will update the list in parent whenever checkboxes change
      final List<dynamic> selectedList;
      
      // Creating a callback function to save state(update list) in 
      // MyHomeWidget
      final void Function(List<dynamic>) callback;
      
      @override
      _InterestsState createState() => _InterestsState();
    }
    
    class _InterestsState extends State<Interests> {
      
      Map<String, dynamic> _categories = {
        "responseCode": "1",
        "responseText": "List categories.",
        "responseBody": [
          {"category_id": "1", "category_name": "Movies"},
          {"category_id": "2", "category_name": "Sports"},
          {"category_id": "3", "category_name": "Food"},
          {"category_id": "4", "category_name": "Music"},
          {"category_id": "5", "category_name": "Others"},
        ],
        "responseTotalResult": 5
      };
    
      void _onCategorySelected(bool selected, categoryId) {
        if (selected == true) {
          setState(() {
            widget.selectedList.add(categoryId);
          });
        } else {
          setState(() {
            widget.selectedList.remove(categoryId);
          });
        }
        
        // Callback to save the updated selectedList to MyHomeWidget list
        widget.callback(widget.selectedList);
      }
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text("Interests"),
          ),
          body: SingleChildScrollView(
            child: Column(
              children: [
                Text(
                  "Select your interests: ",
                  style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
                ),
                ListView.builder(
                  physics: NeverScrollableScrollPhysics(),
                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  itemCount: _categories['responseTotalResult'],
                  itemBuilder: (BuildContext context, int index) {
                    return CheckboxListTile(
                      value: widget.selectedList.contains(
                          _categories['responseBody'][index]['category_id']),
                      onChanged: (bool selected) {
                        _onCategorySelected(selected,
                            _categories['responseBody'][index]['category_id']);
                      },
                      title:
                          Text(_categories['responseBody'][index]['category_name']),
                    );
                  },
                ),
                RaisedButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text("Go back!!"),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    这是您想从Firebase 获取的方法。我使用了更新的类FirebaseFirestore。如果您使用的是旧版本的Firebase,则只需将FirebaseFirestore 替换为Firebase。

    Future<void> fetchInterestChanges() async { //function to get the changes in database.
        final DocumentSnapshot doc = await FirebaseFirestore.instance
            .collection('my_users')
            .document(currentUserModel.id)
            .get();
        
        final updatedList = doc.data();
        print(updatedList);
      }
    

    【讨论】:

    • 谢谢。它解决了目的。这里的技巧是使用回调函数将列表保存在 MyHomeWidget 中,并从 Parent 小部件传递列表。使用提供者也可以达到目的,对吗?考虑到这种情况,哪一个是更好的选择.. setstate 还是 Provider?
    • 是的,你也可以使用Provider。但是对于像你这样的少数小部件,我认为StatefulWidget 可以正常工作。 Provider 应该在您必须在深层层次结构小部件之间访问或传递数据时使用,其中在构造函数中传递参数非常乏味、重复和混乱。
    • 知道了!我确实有另一个问题。上面提到的代码只是一个更大的应用程序的一部分。我必须将选定的兴趣存储在 firestore 数据库中,并使用 firestore 来显示选定的兴趣(我们目前正在通过构造函数传递数据)。您也可以分享我们如何实现这一目标吗?我只是更新问题中的代码
    • 我已经更新了代码并在我编写函数的地方添加了 cmets。如果你能帮我解决这个问题?
    • 您只需要调用get 方法而不是update 从Firebase 获取数据并解析它。另外,我认为您现在使用的是旧版本的Firebase,您必须使用FirebaseFirestore.instance 而不是Firestore.instance。
    【解决方案2】:

    您应该与AutomaticKeepAliveClientMixin 类混合以保存旧的小部件,例如:https://github.com/diegoveloper/flutter-samples/blob/master/lib/persistent_tabbar/page2.dart。

    或者当不工作时:

    1. 您必须将数据复选框保存到缓存中,例如在共享状态等中。
    2. 重新打开页面时,从缓存中调用数据
    3. 当检查/不重新定期数据以缓存

    【讨论】:

      猜你喜欢
      • 2021-12-21
      • 1970-01-01
      • 1970-01-01
      • 2016-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多