Home > Blockchain >  Updating theme when resuming to Flutter app
Updating theme when resuming to Flutter app

Time:07-15

I'm new to Flutter and I'm trying to theme my app dynamically so that the user can select his preferred theme either locally or get the system theme.

I managed to get it to work except for when pausing and then resuming to the app after changing the system theme. The theme doesn't update automatically when resuming and I have to run provider.changeToSystemTheme(); by selecting another setting and setting it back for it to update.

I wasn't able to pass final provider = Provider.of<ThemeProvider>(context); in didChangeAppLifecycleState(AppLifecycleState state) in order to run provider.changeToSystemTheme(); since it dosen't have a context to pass into it.

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

enum SingingCharacter {light, dark, systemDefault}
var systemBrightness;


void main() => runApp(MaterialAppWithTheme());

class MaterialAppWithTheme extends StatefulWidget {
  const MaterialAppWithTheme({Key? key}) : super(key: key);
  @override
  State<MaterialAppWithTheme> createState() => _MaterialAppWithThemeState();
}

class _MaterialAppWithThemeState extends State<MaterialAppWithTheme> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    switch (state) {
      case AppLifecycleState.resumed:
        break;
      case AppLifecycleState.inactive:
        break;
      case AppLifecycleState.paused:
        break;
      case AppLifecycleState.detached:
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => ThemeProvider(),
      builder: (context, _) {
        final themeProvider = Provider.of<ThemeProvider>(context);
        return MaterialApp(
          home: NinjaCard2(),
          themeMode: themeProvider.themeMode,
          theme: MyThemes.lightTheme,
          darkTheme: MyThemes.darkTheme,
        );
      }
    );
  }
}


class ThemeProvider extends ChangeNotifier {
  ThemeMode themeMode = ThemeMode.system;

  void changeToLightTheme() {
    themeMode = ThemeMode.light;
    notifyListeners();
  }

  void changeToDarkTheme() {
    themeMode = ThemeMode.dark;
    notifyListeners();
  }

  void changeToSystemTheme() {
    themeMode = ThemeMode.system;
    notifyListeners();
  }
}

class MyThemes{
  static final darkTheme = ThemeData(
    scaffoldBackgroundColor: Colors.black,
    drawerTheme: DrawerThemeData(backgroundColor: MaterialStateColor.resolveWith((states) => Colors.black)),
    radioTheme: RadioThemeData(fillColor: MaterialStateColor.resolveWith((states) => Colors.blue)),
    dividerColor: Colors.white,
    colorScheme: ColorScheme.dark().copyWith(
      primary: Colors.blue,
      onPrimary: Colors.white,
      secondary: Colors.blue,
      onSecondary: Colors.white,
      surface: Color(0xff303031),
    )
  );
  static final lightTheme = ThemeData(
    colorScheme: ColorScheme.light().copyWith(
      primary: Colors.blue,
      onPrimary: Colors.white,
      secondary: Colors.blue,
      onSecondary: Colors.white,
    )
  );
}

class NinjaCard2 extends StatefulWidget {
  const NinjaCard2({Key? key}) : super(key: key);
  @override
  State<NinjaCard2> createState() => _NinjaCard2State();
}

class _NinjaCard2State extends State<NinjaCard2> {
  SingingCharacter? _character = SingingCharacter.systemDefault;
  @override
  Widget build(BuildContext context) {
    final provider = Provider.of<ThemeProvider>(context);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Settings'),
        elevation: 1.5,
        centerTitle: true,
      ),
      body: Column(
        children: <Widget>[
          ListTile(
            title: const Text('Light'),
            leading: Radio<SingingCharacter>(
              value: SingingCharacter.light,
              groupValue: _character,
              onChanged: (SingingCharacter? value) {
                setState(() {
                  _character = value;
                  provider.changeToLightTheme();
                });
              },
            ),
          ),
          ListTile(
            title: const Text('Dark'),
            leading: Radio<SingingCharacter>(
              value: SingingCharacter.dark,
              groupValue: _character,
              onChanged: (SingingCharacter? value) {
                provider.changeToDarkTheme();
                setState(() {
                  _character = value;
                });
              },
            ),
          ),
          ListTile(
            title: const Text('System Default'),
            leading: Radio<SingingCharacter>(
              value: SingingCharacter.systemDefault,
              groupValue: _character,
              onChanged: (SingingCharacter? value) {
                provider.changeToSystemTheme();
                setState(() {
                  _character = value;
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}

Is there any other way to achieve what looking for?

I would also appreciate links to study materials in the answers.

CodePudding user response:

Inside a State class, you almost always have context available. Besides initState, all the other methods can call it (without any downsides, I mean. Inside initState, you can use context as well, but there are some counter-indications for doing that in some cases).

A full explanation of State: https://api.flutter.dev/flutter/widgets/State-class.html

When in doubt, always look for: https://api.flutter.dev and https://api.dart.dev/

Also, a great way of learning new stuff is over Decoding Flutter playlist: https://youtube.com/playlist?list=PLjxrf2q8roU1fRV40Ec8200rX6OuQkmnl

CodePudding user response:

After checking the State documentation, I was able to find the solution.

All I had to do is add:

@override
void didChangeDependencies() {
  super.didChangeDependencies();
}

after:

@override
void initState() {
  super.initState();
}

Which gives us:

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

enum SingingCharacter {light, dark, systemDefault}

void main() => runApp(MaterialAppWithTheme());

class MaterialAppWithTheme extends StatefulWidget {
  const MaterialAppWithTheme({Key? key}) : super(key: key);
  @override
  State<MaterialAppWithTheme> createState() => _MaterialAppWithThemeState();
}

class _MaterialAppWithThemeState extends State<MaterialAppWithTheme> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => ThemeProvider(),
      builder: (context, _) {
        final themeProvider = Provider.of<ThemeProvider>(context);
        return MaterialApp(
          home: NinjaCard2(),
          themeMode: themeProvider.themeMode,
          theme: MyThemes.lightTheme,
          darkTheme: MyThemes.darkTheme,
        );
      }
    );
  }
}


class ThemeProvider extends ChangeNotifier {
  ThemeMode themeMode = ThemeMode.system;

  void changeToLightTheme() {
    themeMode = ThemeMode.light;
    notifyListeners();
  }

  void changeToDarkTheme() {
    themeMode = ThemeMode.dark;
    notifyListeners();
  }

  void changeToSystemTheme() {
    themeMode = ThemeMode.system;
    notifyListeners();
  }
}

class MyThemes{
  static final darkTheme = ThemeData(
    scaffoldBackgroundColor: Colors.black,
    drawerTheme: DrawerThemeData(backgroundColor: MaterialStateColor.resolveWith((states) => Colors.black)),
    radioTheme: RadioThemeData(fillColor: MaterialStateColor.resolveWith((states) => Colors.blue)),
    dividerColor: Colors.white,
    colorScheme: ColorScheme.dark().copyWith(
      primary: Colors.blue,
      onPrimary: Colors.white,
      secondary: Colors.blue,
      onSecondary: Colors.white,
      surface: Color(0xff303031),
    )
  );
  static final lightTheme = ThemeData(
    colorScheme: ColorScheme.light().copyWith(
      primary: Colors.blue,
      onPrimary: Colors.white,
      secondary: Colors.blue,
      onSecondary: Colors.white,
    )
  );
}

class NinjaCard2 extends StatefulWidget {
  const NinjaCard2({Key? key}) : super(key: key);
  @override
  State<NinjaCard2> createState() => _NinjaCard2State();
}

class _NinjaCard2State extends State<NinjaCard2> {
  SingingCharacter? _character = SingingCharacter.systemDefault;
  @override
  Widget build(BuildContext context) {
    final provider = Provider.of<ThemeProvider>(context);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Settings'),
        elevation: 1.5,
        centerTitle: true,
      ),
      body: Column(
        children: <Widget>[
          ListTile(
            title: const Text('Light'),
            leading: Radio<SingingCharacter>(
              value: SingingCharacter.light,
              groupValue: _character,
              onChanged: (SingingCharacter? value) {
                setState(() {
                  _character = value;
                  provider.changeToLightTheme();
                });
              },
            ),
          ),
          ListTile(
            title: const Text('Dark'),
            leading: Radio<SingingCharacter>(
              value: SingingCharacter.dark,
              groupValue: _character,
              onChanged: (SingingCharacter? value) {
                provider.changeToDarkTheme();
                setState(() {
                  _character = value;
                });
              },
            ),
          ),
          ListTile(
            title: const Text('System Default'),
            leading: Radio<SingingCharacter>(
              value: SingingCharacter.systemDefault,
              groupValue: _character,
              onChanged: (SingingCharacter? value) {
                provider.changeToSystemTheme();
                setState(() {
                  _character = value;
                });
              },
            ),
          ),
        ],
      ),
    );
  • Related