Flutter:即使在屏幕之间导航后也能保存页面状态

Flutter: Saving the state of a page even after navigating between screens

我有一个简单的事情要实现。我的应用程序中有 2 个屏幕,在主屏幕上有一个按钮,可将我导航到一个名为“兴趣”的新页面。兴趣页面是一个复选框列表(为此我必须只使用 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"),
            ),
          ],
        ),
      ),
    );
  }
}

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

或不工作时:

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

您可以从父小部件 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 获取的方法。我已经使用了更新后的 class 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);
  }