Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a simple thing to achieve. I have 2 screens in my app, on the main screen i have a button which navigates me to a new page called Interests. The Interests page is a list of checkboxes(for which i have to use listview.builder only) and a button to submit the data(which navigates me back to the main screen). The thing i want to achieve is this:

  1. checkboxes should work properly.
  2. when i navigate from Interests page to the main page and again navigate back to the Interests page, the selected checkboxes should remain checked. In short the state of the page should be saved.
  3. I have written a function "applyInterestChanges" to save the data in database. I have to retrieve the same data to display the selected checkboxes(which we were doing by passing the data via constructor).

Any help would be appreciated!!

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"),
            ),
          ],
        ),
      ),
    );
  }
}

like image 489
Ankit Singh Avatar asked Oct 27 '25 04:10

Ankit Singh


1 Answers

You can pass an empty list from the parent widget MyHomeWidget & update this list via callback from the Interests widget.

Next time, whenever you go back & navigate again to Interests widget, we will pass this updated list which saves the state of Interests widget. Hence, the checkboxes will be checked depending upon their values in the list.

Here is the implementation:

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!!"),
            ),
          ],
        ),
      ),
    );
  }
}

Here is the method that you wanted to fetch from Firebase. I have used the updated class FirebaseFirestore. If you are using the older version of Firebase, then simply replace FirebaseFirestore with 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);
  }
like image 76
Ravi Singh Lodhi Avatar answered Oct 29 '25 20:10

Ravi Singh Lodhi