How do I change the status bar and Navigation bar themes to match the current theme of a flutter app?

192 Views Asked by At

I am building a flutter mobile app and I would like the status bar and navigation bar themes to match the current theme of the app. The app theme can be changed by the user. Examples in the screenshots attached.

Dark Mode: enter image description here

Light Mode: enter image description here

I used AnnotatedRegion to change the status and navigation bar themes in the MaterialApp. If I am on the home page and change the theme via the drawer, it works as expected, the status bar and the navigation bar changes. However, if I navigate out of the home page by clicking the floating action button, and then change the theme there via the drawer, the status bar and navigation bars themes do not change to match the theme. Note: I used Navigator.of(context) .pushReplacement(MaterialPageRoute(builder: (context) { return const NotificationsScreen(); to move to the Notifications screen. Here is the code:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => SelectThemeProvider(2),
      child:
          Consumer<SelectThemeProvider>(builder: (context, themeProvider, _) {
        bool isDarkMode = themeProvider.selectedTheme == 2;
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Flutter Demo',
          darkTheme: ThemeData.dark(),
          themeMode: currentTheme(themeProvider.selectedTheme),
          theme: ThemeData.light().copyWith(
           
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          ),
          home: AnnotatedRegion<SystemUiOverlayStyle>(
            value: SystemUiOverlayStyle(
              statusBarColor:
                  isDarkMode ? const Color(0xff121212) : Colors.white,
              statusBarIconBrightness:
                  isDarkMode ? Brightness.light : Brightness.dark,
              statusBarBrightness:
                  isDarkMode ? Brightness.dark : Brightness.light,
              systemNavigationBarColor:
                  isDarkMode ? const Color(0xff121212) : Colors.white,
              systemNavigationBarIconBrightness:
                  isDarkMode ? Brightness.light : Brightness.dark,
            ),
            child: const MyHomePage(title: 'Flutter Demo Home Page'),
          ),
        );
      }),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    
    return Scaffold(
      appBar: AppBar(
                title: Text(widget.title),
      ),
      drawer: const MyDrawer(),
      body: Center(
        
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context)
              .pushReplacement(MaterialPageRoute(builder: (context) {
            return const NotificationsScreen();
          }));
        },
        // _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

ThemeMode currentTheme(int choice) {
  switch (choice) {
    case 1:
      return ThemeMode.light;

    case 2:
      return ThemeMode.dark;

    default:
      return ThemeMode.system;
  }
}

//The drawer widget
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_app/select_theme_provider.dart';

class MyDrawer extends StatefulWidget {
  const MyDrawer({super.key});

  @override
  State<MyDrawer> createState() => _MyDrawerState();
}

class _MyDrawerState extends State<MyDrawer> {
  @override
  Widget build(BuildContext context) {
    SelectThemeProvider themeProvider = Provider.of<SelectThemeProvider>(
      context,
      listen: false,
    );
    return Drawer(
      elevation: 3.0,
      width: MediaQuery.of(context).size.width * 0.6,
      child: ListView(
        padding: EdgeInsets.zero,
        children: [
         ListTile(
            leading: const Icon(Icons.home),
            title: const Text('Home'),
            onTap: () {},
          ),
          ListTile(
            leading: const Icon(Icons.settings),
            title: const Text('Settings'),
            onTap: () {
              Navigator.of(context).pop();
            },
          ),
          ListTile(
            leading: const Icon(Icons.person_outline),
            title: const Text('Profile'),
            onTap: () {},
          ),
          Column(
            mainAxisAlignment: MainAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                '         Theme',
                textAlign: TextAlign.center,
                style: Theme.of(context).textTheme.bodyMedium!.copyWith(
                      fontWeight: FontWeight.bold,
                    ),
              ),
              RadioListTile(
                title: const Text('Light'),
                value: 1,
                groupValue: themeProvider.selectedTheme,
                activeColor: Colors.purple,
                onChanged: (value) async {
                  await themeProvider.setThemeMode(value!);
                },
              ),
              RadioListTile(
                title: const Text('Dark'),
                value: 2,
                groupValue: themeProvider.selectedTheme,
                activeColor: Colors.purple,
                onChanged: (value) async {
                  await themeProvider.setThemeMode(value!);
                },
              ),
              RadioListTile(
                title: const Text('System'),
                value: 3,
                groupValue: themeProvider.selectedTheme,
                activeColor: Colors.purple,
                onChanged: (value) async {
                  await themeProvider.setThemeMode(value!);
                },
              ),
            ],
          ),
          ListTile(
            leading: const Icon(Icons.logout),
            title: const Text('Log Out'),
            onTap: () {},
          ),
        ],
      ),
    );
  }
}

//Here is the SelectedThemeProvider

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

class SelectThemeProvider with ChangeNotifier {

  int _selectedTheme;

  // Constructor for the SelectThemeProvider
  SelectThemeProvider(this._selectedTheme);

  int get selectedTheme => _selectedTheme;

  Future<void> setThemeMode(int number) async {
    _selectedTheme = number;
   // SharedPreferences prefs = await SharedPreferences.getInstance();
   // await prefs.setInt('storedTheme', number);
    notifyListeners();
  }

 
}

// Here is the Notifications Screen
import 'package:flutter/material.dart';
import 'package:test_app/my_drawer.dart';

class NotificationsScreen extends StatefulWidget {
  const NotificationsScreen({super.key});

  @override
  State<NotificationsScreen> createState() => _NotificationsScreenState();
}

class _NotificationsScreenState extends State<NotificationsScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      drawer: const MyDrawer(),
      body: const Center(
        child: Text('Notifications feature is coming soon!'),
      ),
    );
  }
}

What is the best way to implement this? I believe that there is an elegant solution someone has that I am missing.

1

There are 1 best solutions below

1
On

First of all you can give custom themes to your application. For this you can check this article (https://mobikul.com/custom-theme-in-flutter/).

ThemeData.dark().copyWith(
      appBarTheme: AppBarTheme(
        color: APPColors.Main.black,
        elevation: 0,
        systemOverlayStyle: SystemUiOverlayStyle.light, // modified system overlay style for dark theme
        iconTheme: IconThemeData(color: APPColors.Main.white),
      ),
      navigationBarTheme: NavigationBarThemeData(
        backgroundColor: APPColors.Main.black, // modified navigation bar's background color
        indicatorColor: APPColors.Main.white,
      ),
)

You can give specific status appbar color and navigation bar color with using this code. This code for dark theme.