0

I am new to flutter and facing issues while trying to incorporate a button on the Appbar that changes the theme of the entire app.

This is the main.dart file code.

import 'package:flutter/material.dart';
import 'package:qrpay_app/routes/Routes.dart';
import 'module/splash/splash_view.dart';
import 'module/buy/buy_list_view.dart';
import 'module/sell/sell_list_view.dart';
import 'module/shop/shop_list_view.dart';
import 'module/account/account_list_view.dart';
import 'module/contact/contact_list_view.dart';
import 'module/help/help_list_view.dart';
import 'module/receipts/receipts_list_view.dart';
import 'module/about us/about_us_view.dart';
import 'package:qrpay_app/Themes/appThemes.dart';

import 'module/qr/qr_view.dart';

void main() {
  runApp(new MaterialApp(
    title: 'QRPAY Touch-Free',
    theme: new ThemeData(primarySwatch: Colors.red),
    home: SplashPage(),
    routes: {
      Routes.splash: (context) => SplashPage(),
      Routes.buy: (context) => BuyPage(),
      Routes.sell: (context) => SellPage(),
      Routes.shop: (context) => ShopPage(),
      Routes.qr: (context) => QRPage(),
      Routes.account: (context) => AccountPage(),
      Routes.contact: (context) => ContactPage(),
      Routes.help: (context) => HelpPage(),
      Routes.receipts: (context) => ReceiptsPage(),
      Routes.about_us: (context) => AboutUsPage(),
    },
  ));
}

This is the appThemes.dart code:

import 'package:flutter/material.dart';

class AppThemes {
  // Simple constructor
  AppThemes._();

  static final ThemeData highContrast = ThemeData(
    primarySwatch: Colors.black,
    primaryTextTheme: TextTheme(
      headline6: TextStyle(color: Colors.white),
    ),
    scaffoldBackgroundColor: Colors.white,
    appBarTheme: AppBarTheme(
      color: Colors.black,
      iconTheme: IconThemeData(
        color: Colors.white,
      ),
      elevation: 30.0,
    ),
  );

  static final ThemeData normalContrast = ThemeData(
    scaffoldBackgroundColor: Colors.white,
    appBarTheme: AppBarTheme(
      color: Colors.red,
      iconTheme: IconThemeData(
        color: Colors.white,
      ),
    ),
  );
}

I have routed the home to splash_view.dart. This is the appThemes.dart code:

import 'package:flutter/material.dart';
import 'package:qrpay_app/widget/drawer.dart';
import 'package:qrpay_app/Themes/appThemes.dart';

class SplashPage extends StatefulWidget {
  static const String routeName = '/splash';
  @override
  _SplashPageState createState() => _SplashPageState();
}

class _SplashPageState extends State<SplashPage> {
  void changeContrast() {
    print("High contrast button clicked\nChanging app contrast\n");
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("QRPAY Touch-Free"),
        elevation: 30.0,
        actions: <Widget>[
          ElevatedButton(
              onPressed: () {
                changeContrast();
              },
              child: Text('High Contrast')),
        ],
      ),
      drawer: AppDrawer(),
      body: new Center(
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Container(
              child: new Image.asset(
                'res/images/qplogo.png',
                width: MediaQuery.of(context).size.width * .9,
                fit: BoxFit.cover,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

I have added an elevated button that needs to change the theme of the entire app. Please help with mapping the functionality based on my dart files.

Also, I am confused about how to add the change in state

I tried working out with 'provider' but I am getting confused.

Please someone help me out. Also, please let me know if you require anything else to better understand my problem.

I appreciate you all taking out the time and helping me out.

Thank You.

lAaravl
  • 929
  • 2
  • 9
  • 20

3 Answers3

1

The simplest approach is to use Provider.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
    MultiProvider(
      providers: [
      // Used MultiProvider incase you have other providers
        ChangeNotifierProvider<ThemeDataProvider>(
          create: (_) => ThemeDataProvider(),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ThemeDataProvider themeDataProvider = Provider.of(context);

    // Pull the theme data from the provider and make a few modification
    // The modifications are for illustration only.  Not required.
    final ThemeData currentTheme = themeDataProvider.themeData.copyWith(
      scaffoldBackgroundColor: themeDataProvider.isDarkTheme ? Colors.yellow[700] : Colors.yellow[300],
      appBarTheme: themeDataProvider.themeData.appBarTheme,
      cardTheme: themeDataProvider.themeData.cardTheme,
    );
    return MaterialApp(
      color: Colors.yellow[100],
      title: 'MyApp',
      theme: currentTheme, //set your theme
      initialRoute: setupRoute,
      onGenerateRoute: Router.generateRoute,
    );
  }
}


class ThemeDataProvider with ChangeNotifier {
  Future<SharedPreferences> _prefs = SharedPreferences.getInstance();

  SharedPreferences prefs;
  bool isInitialized;
  bool _useDarkTheme;
  double _appMargin;
  int _animationDuration = 200;
  ThemeData _themeData;

  ThemeDataProvider() {
    isInitialized = false;
    _initialize();
  }

  void _initialize() async {
    prefs = await _prefs;
    await _loadPrefs();
    _themeData = _buildThemeData();
    _appMargin = 0.0;
    isInitialized = true;
  }

  ThemeData get themeData => _themeData;

  bool get isDarkTheme => _useDarkTheme ?? true;
  double get appMargin => _appMargin;
  int get animDuration => _animationDuration;

  ///
  /// Set the application working margin.
  ///
  void setAppMargin(double appMargin) {
    _appMargin = appMargin;
  }

  void toggleTheme() {
    _useDarkTheme = !_useDarkTheme;
    _savePrefs();
    _themeData = _buildThemeData();
    notifyListeners();
  }

  Future _loadPrefs() async {
    prefs = await _prefs;
    _useDarkTheme = prefs.getBool("useDarkMode") ?? true;
    _themeData = _buildThemeData();
    notifyListeners();
  }

  void _savePrefs() async {
    prefs = await _prefs;
    prefs.setBool("useDarkMode", _useDarkTheme);
  }

// Build your theme data here
      ThemeData _buildThemeData() {
        return ThemeData(
          primarySwatch: isDarkTheme
              ? MaterialColor(4280361249, {
                  50: Color(0xfff2f2f2),
    ...

Add the switch whereever you like and on click call the toggleTheme method.

This is not functionng code. It is an edited version of my production code.

JonnyH
  • 358
  • 2
  • 8
  • I think it is too long for just a simple task or maybe I don't know. I'm new to flutter though for me, I really wish that flutter have a single line of code that could change the theme. – Aquiko May 14 '21 at 05:14
0

You can read an article about Flutter Stream here: https://medium.com/@ayushpguptaapg/using-streams-in-flutter-62fed41662e4

Add a boolean value to track current theme

bool isHighContrast = false;

Then when your switch is on, just simply call:

// switch is on, toggle isHighContrast. isHighContrast now equals to true
controller.add(isHighContrast);

And then listen for changes to update entire UI:

controller.stream.listen({
  setState({
    theme = AppTheme.highContrast;
  });
})
Teddichiiwa
  • 735
  • 6
  • 8
  • how to implement it? do I make changes to each dart file or the file routed at home? Also, how will this change reflect on all the pages of the app? – lAaravl Nov 27 '20 at 01:58
  • You just need to implement it in your MaterialApp's theme property, make sure to put your theme into an variable, so when the contrast was changed, you just call setState and every screen inside your MaterialApp will change – Teddichiiwa Nov 27 '20 at 02:59
  • I have changed the question please review it, also I need the button to change the theme when depending on the current state, If it's in normal mode then switch to high contrast and if it's in high contrast mode then switch to normal mode. – lAaravl Nov 27 '20 at 11:07
0

I use StateProvider in flutter_riverpod package to achieve this.

// Add the global provider
final appThemeProvider = StateProvider<ThemeData>((ref) {
  return App.getDefaultThemeData();
});

class App extends ConsumerWidget {
//                ^^^^^^^^^^^^^^
  @override
  Widget build(BuildContext context, WidgetRef ref) {
//                                   ^^^^^^^^^^^^^
    final currentTheme = ref.watch(appThemeProvider).state;

    return MaterialApp(
      color: Colors.yellow[100],
      title: 'MyApp',
      theme: currentTheme, //set your theme
      initialRoute: setupRoute,
      onGenerateRoute: Router.generateRoute,
    );

    // this is default theme
    static ThemeData getDefaultThemeData() {
      return ThemeData(
        primarySwatch: MaterialColor(0xFF00FF00, mappingColor),
        backgroundColor: MaterialColor(0xFFFFFEFA, mappingColor),
      );
    }

    // use this method to changeTheme
    void _changeTheme({required WidgetRef ref, required ThemeData themeData}) {
      Future.delayed(const Duration(milliseconds: 10), () {
        ref.read(appThemeProvider).state = themeData;
      }
    }
} 
Akabrando
  • 176
  • 1
  • 9