This article is collected in Zhang Fengjie, the king of public number programming: article memory address F-S-A-04,
For more information on dry food, see << Programming King Food Specification 1.0>>
Many Flutter state management articles are about changing counters, which can often feel like a nuisance. Make too complex examples, an article is not realistic. Let’s take the theme color switch + internationalization. In this article, we’ll look at three ways provoder, BLoC and Redux have implemented the theme color switch + internationalization, hence the triple combo.
I. Provoder achieves subject change and internationalization:The provider: ^ 03.1.0 + 1
1- Theme color switch
Click the color switch button to switch the global theme colors.
1.1 state class
Since it’s state management, let’s first look at states. There is no doubt about the color. There is also a color selection index, which shows how the color button is selected. Inheriting from ChangeNotifier, state variables are treated as properties, using changeThemeData to change the state variables, and notifying the people who need them to refresh.
---->[provider/theme_state.dart]---- class ThemeState extends ChangeNotifier{ ThemeData _themeData; // Theme int _colorIndex; / / theme ThemeState (enclosing _colorIndex, enclosing _themeData,); void changeThemeData(int colorIndex,ThemeData themeData){ _themeData = themeData; _colorIndex = colorIndex; notifyListeners(); } ThemeData get themeData => _themeData; Int get colorIndex => _colorIndex; // Get the number}Copy the code
1.2- Top parcel
The template for the state management library is basically the same, wrapping up the parts that need to be managed. In this case, you can directly use multiple provider packers. To make it look good, create a new Wrapper component to wrap it.
void main() => runApp(Wrapper(child:MyApp())); class Wrapper extends StatelessWidget { final Widget child; Wrapper({this.child}); @Override Widget build(BuildContext context) {final initThemeData= ThemeData(// initial theme Color: color.blue,); final initIndex=4; // Start index return MultiProvider(providers: [ChangeNotifierProvider(builder: (_) => ThemeState(initIndex,initThemeData)), // provide provider here], child: child, // child); }}Copy the code
1.3- Use state and call methods
Provider. Of (context).themeData retrivesThemeData, but to reduce the granularity of the build, Consumer is used for point-to-point consumption.
---->[main.dart]---- class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer<ThemeState>(Builder: (_,state,__)=>MaterialApp(// 主 题 目 标 title: 'Flutter Demo', theme: Home: MyHomePage(),); home: MyHomePage(); } } ---->[pages/home_page.dart]---- children: <Widget>[ Consumer<ThemeState>(builder: (_, the state, __) = > Text (' - on the other side of the sea, I have never witness presence ', style: TextStyle (color: state. ThemeData. PrimaryColor, fontSize: 18, fontWeight: FontWeight.bold), ...Copy the code
So wherever the color is needed, you can use this method to take the theme color from the state, and it is very easy to trigger the color toggle event. ColorChooser is my custom component that calls back the index and color value when clicked and fires the changeThemeData method to update the consumer’s state.
var colors = Consumer<ThemeState>(builder: (_,state,__)=>ColorChooser( colors: Cons.THEME_COLORS, initialIndex: State. ColorIndex,// Synchronize index status onChecked: (I,color) {ThemeData ThemeData = ThemeData(primaryColor: color); / / color state. ChangeThemeData (I, themeData); // Trigger event},));Copy the code
So the theme switch and the color switch are OK
2- Language switch Switch
Click the sidebar button to switch languages
Dependencies: # Internationalization SDK: FlutterCopy the code
2.1- Prepare the data first
Static final EN={"title":"ZF·G·Toly ", "subTitle":"---- You are nothing at all", "content":"public: The King Of Coder", "sideTitle":"I Have a Dream", "step1":"Unified the Earth", "step2":"Unified the Solar System", "step3":"Unified the Galaxy", "step4":"Unified the Universe", "step5":"Unified All Universe", Step4SubTitle :"To be the king of Universe", "step4Info":"A.D. 34679, Toly Unified the Universe, Be the first omniscient." , "btn2CN":" Switch Chinese." , "btn2EN":"To English." }; Static final ZN={"title", "subTitle":"---- the other side of the sea, there is I have not seen the style ", "content":" public number: code king ", "sideTitle":" list a small goal ", Step1: "the earth", "step2" : "unity of the solar system", "step 3" : "unified the Milky Way", "step4" : "unity of the universe", "step5:" "unified parallel universe", "step4SubTitle" : "to be king of the universe". "Step4Info ":" In AD 34679, Jet unified the known universe and became the first omniscient." , "btn2CN":" Switch Chinese." , "btn2EN":"To English." }; }Copy the code
2.2- then I wrote a utility class to generate related code with one key
After running, the following files are automatically generated:
---->[I18N agent related]---- /// /Power By zhang Feng Jiedai -- Generated file. Do not edit. import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'i18n.dart'; Class I18nDelegate extends LocalizationsDelegate<I18N> {I18nDelegate(); @override bool isSupported(Locale Locale) {return ['en', 'en']. Contains (locale.languagecode); } @override Future<I18N> load(Locale Locale) {return SynchronousFuture<I18N>(I18N(Locale)); } @override bool shouldReload(LocalizationsDelegate<I18N> old) { return false; } static I18nDelegate delegate = I18nDelegate(); } -- -- -- - > [I18N using class] - / / / Power By packer fierce - jet Generated file. Do not edit, import 'package: flutter/material. The dart'; import 'data.dart'; class I18N { final Locale locale; I18N(this.locale); Static Map<String, Map<String,String>> _localizedValues = {'en': data. en,//英文 'en': data. ZN,// 英文 'en': data. ZN}; static I18N of(BuildContext context) { return Localizations.of(context, I18N); } get title { return _localizedValues[locale.languageCode]['title']; } get subTitle { return _localizedValues[locale.languageCode]['subTitle']; } get content { return _localizedValues[locale.languageCode]['content']; } get sideTitle { return _localizedValues[locale.languageCode]['sideTitle']; } get step1 { return _localizedValues[locale.languageCode]['step1']; } get step2 { return _localizedValues[locale.languageCode]['step2']; } get step3 { return _localizedValues[locale.languageCode]['step3']; } get step4 { return _localizedValues[locale.languageCode]['step4']; } get step5 { return _localizedValues[locale.languageCode]['step5']; } get step4SubTitle { return _localizedValues[locale.languageCode]['step4SubTitle']; } get step4Info { return _localizedValues[locale.languageCode]['step4Info']; } get btn2CN { return _localizedValues[locale.languageCode]['btn2CN']; } get btn2EN { return _localizedValues[locale.languageCode]['btn2EN']; }}Copy the code
2.3 state class
One field is simple, and for ease of use, we define two Factories to quickly generate objects.
class LocaleState extends ChangeNotifier{ Locale _locale; / / theme LocaleState (enclosing _locale); factory LocaleState.zh()=> LocaleState(Locale('zh', 'CH')); factory LocaleState.en()=> LocaleState(Locale('en', 'US')); void changeLocaleState(LocaleState state){ _locale=state.locale; notifyListeners(); } Locale get locale => _locale; // Get the language}Copy the code
2.4 – use
Consumer2 can be used if a component has more than one status value, up to six.
You can also use Provider. Of (context) to retrieve the state class
---->[main.dart add provider]---- return MultiProvider(providers: [ChangeNotifierProvider(Builder: (_) => ThemeState(initIndex,initThemeData)), // Provide provider ChangeNotifierProvider(Builder: (_) = > LocaleState. Useful ()), / / in this provider], child: the child, / / child); ---->[MaterialApp for international configuration]---- class MyApp extends StatelessWidget {@Override Widget build(BuildContext context) { Return Consumer2<ThemeState, LocaleState>(Builder: (_, ThemeState, LocaleState, __) => MaterialApp(// 'Flutter Demo', localizationsDelegates: [GlobalMaterialLocalizations. Delegate, GlobalWidgetsLocalizations. Delegate, I18nDelegate. Delegate, / / add], locale: Localestation. locale, supportedLocales: [localestation. locale], theme: themeState. ThemeData, // Get data home: MyHomePage(), )); }} - > the use of [international], Consumer < ThemeState > (builder: (_, the state, __) = > Text (I18N. Of (context). The subTitle, / / get the style: TextStyle( color: state.themeData.primaryColor, fontSize: 18, fontWeight: FontWeight.bold), ),), ---->[Behavior triggering]---- state.ChangelocalEstate (localEstation.zh ()) state.ChangelocalEstate (localEstation.zh ())Copy the code
This demonstrates how the Provider works in the case of multiple states.
Redux to achieve theme switching and internationalization:Flutter_redux: ^ 0.5.3
As a global state management repository for data sources, Redux takes a standard approach. The general state, as the son of heaven, then divided the tasks to the major vassals, and the vassals also divided the tasks to the doctor. When everyone manages his own responsibility, then the world peace, endless. There are only two states, theme color and internationalization.
1-3 big story
Click the color switch button to switch the global theme colors. The idea is very much the same, so let’s look at the differences, starting with the three main components of Rudux: State, Action, and processor Reducer. All states are managed by the warehouse, and the state AppState is sealed down.
When defining the redux state, I like to define an initial state for ease of use. Or you can just build it as you use it.
---->[global redux]---- class AppState {final ThemeState ThemeState; // Final LocaleState LocaleState; AppState({this.themestate, this.localestate}); // AppState({this.themestate, this.localestate}); factory AppState.initial()=> AppState( themeState: ThemeState.initial(), localeState: LocaleState.initial() ); } // Total processor -- bundle responsibility AppState appReducer(AppState prev, dynamic action)=> AppState( themeState:themeDataReducer(prev.themeState, action), localeState: localReducer(prev.localeState, action),);Copy the code
---->[theme redux]---- class ThemeState extends ChangeNotifier {ThemeData ThemeData; // Theme int colorIndex; ThemeState(this.colorindex, this.themedata,); factory ThemeState.initial()=> ThemeState(4, ThemeData(primaryColor: Colors.blue,)); } class ActionSwitchTheme {final ThemeData ThemeData; final int colorIndex; ActionSwitchTheme(this.colorIndex, this.themeData); Var themeDataReducer = TypedReducer<ThemeState, ActionSwitchTheme>((state, action) => ThemeState(action.colorIndex, action.themeData,));Copy the code
---->[internationalized redux]---- // Switch language status class LocaleState{Locale Locale; / / theme LocaleState (enclosing the locale); factory LocaleState. initial()=> LocaleState(Locale('zh', 'CH')); } class ActionSwitchLocal {final Locale Locale; ActionSwitchLocal(this.locale); factory ActionSwitchLocal.zh()=> ActionSwitchLocal(Locale('zh', 'CH')); factory ActionSwitchLocal.en()=> ActionSwitchLocal(Locale('en', 'US')); Var localReducer = TypedReducer<LocaleState, ActionSwitchLocal>((state, action) => LocaleState(action.locale,));Copy the code
2- Redux properties used
Redux needs to be wrapped with a StoreProvider, where the repository is configured under the Store property.
Storebuilders exist just like consumers in Providers, except generics are a unified AppState.
void main() => runApp(Wrapper(child: MyApp())); class Wrapper extends StatelessWidget { final Widget child; Wrapper({this.child}); @override Widget build(BuildContext context) { return StoreProvider( store: Store<AppState>( appReducer, initialState: Appstate.initial (),// initial state), child:child); } } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return StoreBuilder<AppState>(Builder: (context, store) => MaterialApp(// 主 题 目 主 题 目 title: 'Flutter Demo', localizationsDelegates: [GlobalMaterialLocalizations. Delegate, GlobalWidgetsLocalizations. Delegate, I18nDelegate. Delegate, / / add], locale: store.state.localeState.locale, supportedLocales: [ store.state.localeState.locale ], theme: Store. State. ThemeState themeData, / / get data home: MyHomePage ())); }}Copy the code
In use, both state and event distribution are managed uniformly by the warehouse, and the results are consistent:
----> ---- StoreBuilder<AppState>(Builder: (_, store) =>Text(i18n. of(context).subtitle, style: TextStyle (color: store. State. ThemeState. ThemeData. PrimaryColor, / / through data warehouse with fontSize: 18, fontWeight: ---- var colors = StoreBuilder<AppState>(Builder: (_, store) => colorcolorchooser (colors: Cons. THEME_COLORS initialIndex: store. State. ThemeState. ColorIndex, / / synchronization state index onChecked: (i,color) { ThemeData themeData = ThemeData(primaryColor: color); / / color store. Dispatch (ActionSwitchTheme (I, themeData)); // Trigger event},));Copy the code
The benefit of Redux is that state resources are managed uniformly. Layer upon layer sealed, clear structure.
BLoC implementation theme change and internationalization:Flutter_bloc: ^ 0.22.1
If Redux is centralised and decentralised, BloC is a complete liberal democracy. An BloC also has three major components: BloC business logic unit, State State, and Events
1. BloC with theme color
State class
You can write your own style according to your own hobby. Here are my favorite styles. Put the state quantity in the abstract class, other states to inherit it to achieve the state differentiation. You can also add some common states if you want.
@immutable abstract class ThemeState { final ThemeData themeData; // Final int colorIndex; / / digital ThemeState (enclosing colorIndex, enclosing themeData); } class InitialThemeState extends ThemeState { InitialThemeState() : super(4, ThemeData(primaryColor: Colors.blue,)); } class ThemeStateImpl extends ThemeState { ThemeStateImpl(int colorIndex, ThemeData themeData) : super(colorIndex, themeData); }Copy the code
Event classes
Define the events that Bloc can execute, such as switching and resetting the status with two parameters
@immutable abstract class ThemeEvent {} class EventSwitchTheme extends ThemeEvent{ final ThemeData themeData; // Final int colorIndex; / / digital EventSwitchTheme (enclosing colorIndex, enclosing themeData); } class EventResetTheme extends ThemeEvent{}Copy the code
Business logic unit class
This is the core of Bloc, a state that is generated mainly through events.
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> { @override ThemeState get initialState => InitialThemeState(); @Override Stream<ThemeState> mapEventToState(ThemeEvent Event,) async* {// Use async generator if(Event is EventSwitchTheme) {/ / if the switch is subject matter, generate the corresponding ThemeState yield ThemeStateImpl (event. ColorIndex, event. ThemeData); } if(event is EventResetTheme){// Generate initialState yield InitialThemeState(); }}}Copy the code
2. International BloC
State class
@immutable
abstract class LocaleState {
final Locale locale;
LocaleState(this.locale);
}
class InitialLocaleState extends CnLocaleState {}
class CnLocaleState extends LocaleState {
CnLocaleState() : super(Locale('zh', 'CH'));
}
class EnLocaleState extends LocaleState {
EnLocaleState() : super(Locale('en', 'US'));
}
Copy the code
Event classes
@immutable
abstract class LocaleEvent {}
class EventSwitch2CN extends LocaleEvent{}
class EventSwitch2EN extends LocaleEvent{}
Copy the code
Business logic unit class
class LocaleBloc extends Bloc<LocaleEvent, LocaleState> { @override LocaleState get initialState => InitialLocaleState(); @override Stream<LocaleState> mapEventToState(LocaleEvent event,) async* { if(event is EventSwitch2CN){// To switch to CN, generate CnLocaleState yield CnLocaleState(); } if(event is EventSwitch2EN){// Generate EnLocaleState yield EnLocaleState(); }}}Copy the code
3. The use of the Bloc
They are all very similar, with the outer layer :MultiBlocProvider
void main() => runApp(Wrapper(child: MyApp())); class Wrapper extends StatelessWidget { final Widget child; Wrapper({this.child}); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider<ThemeBloc>(builder: (context) => ThemeBloc(),), BlocProvider<LocaleBloc>(builder: (context) => LocaleBloc(),), ], child: MyApp() ); } } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return BlocBuilder<ThemeBloc, ThemeState>(builder: (_, theme) => BlocBuilder<LocaleBloc, LocaleState>(builder: (_, local) => MaterialApp(// 主 题 目 主 题 目 title: 'Flutter Demo', localizationsDelegates: [GlobalMaterialLocalizations. Delegate, GlobalWidgetsLocalizations. Delegate, I18nDelegate. Delegate, / / add], locale: Local.locale, supportedLocales: [local.locale], theme: theme. ThemeData, home: MyHomePage())); }}Copy the code
BlocBuilder
,>
(Builder: (_, theme)
(builder: (_, state) =>Text(i18n. of(context).subtitle, style: TextStyle( color: state.themeData.primaryColor, fontSize: 18, fontWeight: Bold),),), ---->[distribution event]---- var colors = BlocBuilder<ThemeBloc, ThemeState>(Builder: (_, state) = > ColorChooser (colors: Cons. THEME_COLORS, initialIndex: state colorIndex, / / synchronization state index onChecked: (i,color) { ThemeData themeData = ThemeData(primaryColor: color); Of <ThemeBloc>(context). Add (EventSwitchTheme(I, themeData)); // Trigger event},));Copy the code
On the whole, more of the same. BloC can feel very elegant if the Stream is well understood. Personally, I prefer redux. Provider, as an official announcement, is also very useful. If you can hold it, you can mix it. It is understood in this article that you have only just begun to manage your Flutter state. There’s a long way to go…
conclusion
As we come to the end of this article, if you want to have a quick taste of the new Flutter, Seven Days of Flutter is for you. If you want to explore it, follow my footsteps and take a Flutter tour.
In addition, I have a Wechat communication group with Flutter. Welcome to join my wechat group to discuss the problem of Flutter. My wechat signal is ZDL1994328. In addition, welcome to pay attention to the king of public number programming