rusty
rusty

Reputation: 402

Update the Drawer contents based on settings page changes in Flutter

I need to refresh the drawer contents to apply, specifically, the font size changes made in the settings page. The drawer is defined in the MyHomePage class. A portion of my code is below. I need a way to call setState when the user returns back from the settings page. To load the settings page my code uses the mechanism shown in the code below; it does not use Navigator. When using a TextButton in the dashboard page, I can navigate to the settings page using Navigator and pop back and get the settings applied by the comment of @johandanmo in github issue where he states to use the code:

Navigator.push( context, MaterialPageRoute( builder: (context) => SecondPage()), ).then((value) => setState(() {  //  state update codes goes here }));

But, I need to show the settings page item in the Drawer so I can not use the above. How can I refresh the page or call setState given the following implementation?

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var currentPage = DrawerSections.dashboard;

  @override
  Widget build(BuildContext context) {
    var container;
    switch (currentPage) {
      case DrawerSections.dashboard:
        container = const DashboardPage();
        break;
      case DrawerSections.settings:
        container = const MySettingsPage();
        break;
    }
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: container,
      drawer: Drawer(
        child: SingleChildScrollView(
          child: Container(
            child: Column(
              children: [
                // const MyHeaderDrawer(),
                myDrawerList(),
              ],
            ),
          ),
        ),
      ),
    );
  }

  ... ... ...

}

UPDATE 01:

I have imported provider package and written some codes that I'll post below.

The following is the body of my _MyHomePageState class:

body: Consumer<SettingsProvider>(
        builder: (context, provider, child) {
          // Since isSettingsChangesAppliedTo_Screen__Menu_Page is a static,
          // variable, we need not use an instance "provider" to access it, it
          // can be directly accessed using the class name.
          // if (provider.isSettingsChangesAppliedTo_Screen__Menu_Page)
          if (SettingsProvider.isSettingsChangesAppliedTo_Screen__Menu_Page) {
            setState(() {
              applyNewSettings();
            });
            provider.resetSettingsStatusChange();
          }
          return container;
        },
      ),

The contents of my provider_settings.dart page is:

import 'package:flutter/material.dart';

class SettingsProvider with ChangeNotifier {
  static bool isSettingsChangesAppliedTo_Screen__Menu_Page = false;

  bool checkSettingsStatusChangeAndNotify() {
    if (isSettingsChangesAppliedTo_Screen__Menu_Page) {
      notifyListeners();
      return true;
    } else {
      // In case isSettingsChangesAppliedTo_Screen__Menu_Page is false, we need
      // not, even call the notifyListeners() function. Calling for now, just in
      // case.
      notifyListeners();
      return false;
    }
  }

  void resetSettingsStatusChange() {
    isSettingsChangesAppliedTo_Screen__Menu_Page = false;
  }
}

I properly update the static variable isSettingsChangesAppliedTo_Screen__Menu_Page once settings get changed.

I am getting exception with this update to my actual code. I've created this minimal app source code in which there is no exception message but the app remains in splash screen for ever.

UPDATE 02:

Upon using the suggested code, in answer, I returned Text from the provider's builder as:

body: Consumer<SettingsProvider>(
        builder: (context, SettingsProvider provider, child) {
          return Text(
            "Hello, this is a test text.",
            style: TextStyle(
              fontSize: provider.fontSize,
            ),
          );
        },
      ),

This resulted in exception. A snapshot of the call stack is below:

call_stack_snapshot

Upvotes: 0

Views: 70

Answers (1)

Phoenix
Phoenix

Reputation: 145

For what i know, there are (at least) two possible implementation for your use case:

  1. Use MVP pattern (or kinda): You need to instantiate your HomePage somewhere (maybe in the main) and pass it to the settings page. Your HomePage needs to provide a method for the view update, which will be called from your settings page when you change the font.
  2. Use state management apprach (and i think this is the staple in flutter): You can use one of many state management to do this, like Provider pattern, Bloc, ecc. I can advice using Provider pattern which is simple and fast to learn. What will you get is an object that will stay alive and where you can get the data from. In this way, your HomePage needs to listen to the Provider, which will alert all his listeners with a NotifyListener() call when you'll change some settings. This approach will permit you to adapt you SettingPage class (just make a Provider to save the settings) and permits you to expand it for all the settings you need, which would be more complicated with the prevous approach.

Follow this tutorial to understand and learn how to use it.

(When he speaks about ChangeNotifiers, it's a component of Provider pattern).

UPDATE 1: You're not using the provider properly. You created the provider, but because you're setting the variable you want to change directly, without calling any method, you won't call notifyListener (and you don't need that variable to be static). More, you don't have to call setState anymore. You'll rely on data inside the provider, so if it changes, notifyListener tells to the view to change it accordingly.

In this code, you have to save you data in the provider. Try modifying you code like this

import 'package:flutter/material.dart';

class SettingsProvider with ChangeNotifier {
  
double fontSize = 16.0;

void changeFontSize(double value){
   fontSize = value;
   notifyListeners();
}

  void resetSettingsStatusChange() {
    fontSize = 16.0;
    notifyListeners();
  }
}

Then, you homePage will rely on the data in the provider, so for example:

body: Consumer<SettingsProvider>(
        builder: (context,SettingsProvider provider, child) {
         
          Text('Hello, this is a test text',
           style: TextStyle(fontSize: provider.fontSize));
        },
      ),

In this way, changing data in the provider will affect your view. What were you doing before was wrong. I advice you to watch videos and learn how to use it (it helped me).

Upvotes: 1

Related Questions