Last active
December 25, 2019 02:10
flutter redux demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://github-riskers-blog.oss-cn-qingdao.aliyuncs.com/20190117210031.gif | |
import 'package:flutter/material.dart'; | |
import 'package:redux/redux.dart'; | |
import 'package:redux_thunk/redux_thunk.dart'; | |
import 'package:flutter_redux/flutter_redux.dart'; | |
class AppState { | |
final int count; | |
final int clickCount; | |
AppState({ | |
this.count, | |
this.clickCount, | |
}); | |
// https://github.com/johnpryan/redux.dart/issues/21 | |
AppState copyWith({count, clickCount}) { | |
return AppState( | |
count: count ?? this.count, | |
clickCount: clickCount ?? this.clickCount, | |
); | |
} | |
} | |
// actions | |
enum Actions { | |
Click, | |
Increment, | |
Decrement, | |
} | |
// reducers: | |
AppState counterReducer(AppState state, dynamic action) { | |
switch (action) { | |
case Actions.Increment: | |
return state.copyWith(count: state.count + 1); | |
case Actions.Decrement: | |
return state.copyWith(count: state.count - 1); | |
} | |
return state; | |
} | |
AppState valueReducer(AppState state, dynamic action) { | |
if (action == Actions.Click) { | |
return state.copyWith(clickCount: state.clickCount + 1); | |
} | |
return state; | |
} | |
final reducers = combineReducers<AppState>([ | |
counterReducer, | |
valueReducer, | |
]); | |
void main() { | |
// store: | |
final store = new Store<AppState>( | |
reducers, | |
middleware: [thunkMiddleware], | |
initialState: AppState( | |
count: 0, | |
clickCount: 0, | |
), | |
); | |
runApp(FlutterReduxApp( | |
title: 'Flutter Redux Demo', | |
store: store, | |
)); | |
} | |
class AppStateViewModel { | |
final AppState state; | |
final void Function() onClick; | |
AppStateViewModel({ | |
this.state, | |
this.onClick, | |
}); | |
} | |
class SimpleText extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<AppState, AppStateViewModel>( | |
converter: (store) { | |
return AppStateViewModel( | |
state: store.state, | |
); | |
}, | |
builder: (context, vm) { | |
String count = vm.state.count.toString(); | |
String clickCount = vm.state.clickCount.toString(); | |
return new Text( | |
'count: $count, clickCount: $clickCount', | |
style: Theme.of(context).textTheme.subhead, | |
); | |
}, | |
); | |
} | |
} | |
class AddButton extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<AppState, AppStateViewModel>( | |
converter: (store) { | |
return AppStateViewModel( | |
onClick: () { | |
store.dispatch(Actions.Increment); | |
store.dispatch(Actions.Click); | |
} | |
); | |
}, | |
builder: (context, vm) { | |
return Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: new FloatingActionButton( | |
// Attach the `callback` to the `onPressed` attribute | |
onPressed: vm.onClick, | |
tooltip: 'Increment', | |
child: new Icon(Icons.exposure_plus_1), | |
), | |
); | |
}, | |
); | |
} | |
} | |
class DesButton extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<AppState, AppStateViewModel>( | |
converter: (store) { | |
return AppStateViewModel( | |
onClick: () { | |
store.dispatch(Actions.Decrement); | |
} | |
); | |
}, | |
builder: (context, vm) { | |
return Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: new FloatingActionButton( | |
// Attach the `callback` to the `onPressed` attribute | |
onPressed: vm.onClick, | |
tooltip: 'des', | |
child: new Icon(Icons.exposure_neg_1), | |
), | |
); | |
}, | |
); | |
} | |
} | |
class FlutterReduxApp extends StatelessWidget { | |
final Store<AppState> store; | |
final String title; | |
FlutterReduxApp({Key key, this.store, this.title}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
// The StoreProvider should wrap your MaterialApp or WidgetsApp. This will | |
// ensure all routes have access to the store. | |
return new StoreProvider<AppState>( | |
// Pass the store to the StoreProvider. Any ancestor `StoreConnector` | |
// Widgets will find and use this value as the `Store`. | |
store: store, | |
child: MaterialApp( | |
theme: ThemeData.dark(), | |
title: title, | |
home: Scaffold( | |
appBar: AppBar( | |
title: new Text(title), | |
), | |
body: new Center( | |
child: new Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
new Text( | |
'You have pushed the button this many times:', | |
), | |
// Connect the Store to a Text Widget that renders the current | |
// count. | |
// | |
// We'll wrap the Text Widget in a `StoreConnector` Widget. The | |
// `StoreConnector` will find the `Store` from the nearest | |
// `StoreProvider` ancestor, convert it into a String of the | |
// latest count, and pass that String to the `builder` function | |
// as the `count`. | |
// | |
// Every time the button is tapped, an action is dispatched and | |
// run through the reducer. After the reducer updates the state, | |
// the Widget will be automatically rebuilt with the latest | |
// count. No need to manually manage subscriptions or Streams! | |
SimpleText(), | |
], | |
), | |
), | |
// Connect the Store to a FloatingActionButton. In this case, we'll | |
// use the Store to build a callback that with dispatch an Increment | |
// Action. | |
// | |
// Then, we'll pass this callback to the button's `onPressed` handler. | |
floatingActionButton: Row( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
AddButton(), | |
DesButton(), | |
], | |
), | |
), | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:redux/redux.dart'; | |
import 'package:redux_thunk/redux_thunk.dart'; | |
import 'package:flutter_redux/flutter_redux.dart'; | |
enum Actions { Increment } | |
// The reducer, which takes the previous count and increments it in response | |
// to an Increment action. | |
int counterReducer(int state, dynamic action) { | |
if (action == Actions.Increment) { | |
return state + 1; | |
} | |
return state; | |
} | |
void main() { | |
// Create your store as a final variable in a base Widget. This works better | |
// with Hot Reload than creating it directly in the `build` function. | |
final store = new Store<int>( | |
counterReducer, | |
middleware: [thunkMiddleware], | |
initialState: 0 | |
); | |
runApp(FlutterReduxApp( | |
title: 'Flutter Redux Demo', | |
store: store, | |
)); | |
} | |
class Simple extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<int, String>( | |
converter: (store) => store.state.toString(), | |
builder: (context, count) { | |
return new Text( | |
count, | |
style: Theme.of(context).textTheme.display1, | |
); | |
}, | |
); | |
} | |
} | |
class AddButton extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<int, VoidCallback>( | |
converter: (store) { | |
// Return a `VoidCallback`, which is a fancy name for a function | |
// with no parameters. It only dispatches an Increment action. | |
return () => store.dispatch(Actions.Increment); | |
}, | |
builder: (context, callback) { | |
return new FloatingActionButton( | |
// Attach the `callback` to the `onPressed` attribute | |
onPressed: callback, | |
tooltip: 'Increment', | |
child: new Icon(Icons.add), | |
); | |
}, | |
); | |
} | |
} | |
class FlutterReduxApp extends StatelessWidget { | |
final Store<int> store; | |
final String title; | |
FlutterReduxApp({Key key, this.store, this.title}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
// The StoreProvider should wrap your MaterialApp or WidgetsApp. This will | |
// ensure all routes have access to the store. | |
return new StoreProvider<int>( | |
// Pass the store to the StoreProvider. Any ancestor `StoreConnector` | |
// Widgets will find and use this value as the `Store`. | |
store: store, | |
child: MaterialApp( | |
theme: ThemeData.dark(), | |
title: title, | |
home: Scaffold( | |
appBar: AppBar( | |
title: new Text(title), | |
), | |
body: new Center( | |
child: new Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
new Text( | |
'You have pushed the button this many times:', | |
), | |
// Connect the Store to a Text Widget that renders the current | |
// count. | |
// | |
// We'll wrap the Text Widget in a `StoreConnector` Widget. The | |
// `StoreConnector` will find the `Store` from the nearest | |
// `StoreProvider` ancestor, convert it into a String of the | |
// latest count, and pass that String to the `builder` function | |
// as the `count`. | |
// | |
// Every time the button is tapped, an action is dispatched and | |
// run through the reducer. After the reducer updates the state, | |
// the Widget will be automatically rebuilt with the latest | |
// count. No need to manually manage subscriptions or Streams! | |
Simple(), | |
], | |
), | |
), | |
// Connect the Store to a FloatingActionButton. In this case, we'll | |
// use the Store to build a callback that with dispatch an Increment | |
// Action. | |
// | |
// Then, we'll pass this callback to the button's `onPressed` handler. | |
floatingActionButton: AddButton(), | |
), | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:redux/redux.dart'; | |
import 'package:redux_thunk/redux_thunk.dart'; | |
import 'package:flutter_redux/flutter_redux.dart'; | |
class AppState { | |
final int count; | |
final int clickCount; | |
AppState({ | |
this.count, | |
this.clickCount, | |
}); | |
AppState copyWith({count, clickCount}) { | |
return AppState( | |
count: count ?? this.count, | |
clickCount: clickCount ?? this.clickCount, | |
); | |
} | |
} | |
enum Actions { | |
Click, | |
Increment, | |
Decrement, | |
} | |
ThunkAction<AppState> asyncIncrement() { | |
return (Store<AppState> store) async { | |
await Future.delayed(Duration(seconds: 2)); | |
store.dispatch(Actions.Increment); | |
}; | |
} | |
AppState counterReducer(AppState state, dynamic action) { | |
switch (action) { | |
case Actions.Increment: | |
return state.copyWith(count: state.count + 1); | |
case Actions.Decrement: | |
return state.copyWith(count: state.count - 1); | |
} | |
return state; | |
} | |
AppState valueReducer(AppState state, dynamic action) { | |
if (action == Actions.Click) { | |
return state.copyWith(clickCount: state.clickCount + 1); | |
} | |
return state; | |
} | |
final reducers = combineReducers<AppState>([ | |
counterReducer, | |
valueReducer, | |
]); | |
void main() { | |
final store = new Store<AppState>( | |
reducers, | |
middleware: [thunkMiddleware], | |
initialState: AppState( | |
count: 0, | |
clickCount: 0, | |
), | |
); | |
runApp(FlutterReduxApp( | |
title: 'Flutter Redux Demo', | |
store: store, | |
)); | |
} | |
class AppStateViewModel { | |
final AppState state; | |
final void Function() onClick; | |
AppStateViewModel({ | |
this.state, | |
this.onClick, | |
}); | |
} | |
class SimpleText extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<AppState, AppStateViewModel>( | |
converter: (store) { | |
return AppStateViewModel( | |
state: store.state, | |
); | |
}, | |
builder: (context, vm) { | |
String count = vm.state.count.toString(); | |
String clickCount = vm.state.clickCount.toString(); | |
return new Text( | |
'count: $count, clickCount: $clickCount', | |
style: Theme.of(context).textTheme.subhead, | |
); | |
}, | |
); | |
} | |
} | |
class AddButton extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<AppState, AppStateViewModel>( | |
converter: (store) { | |
return AppStateViewModel( | |
onClick: () { | |
store.dispatch(Actions.Increment); | |
store.dispatch(Actions.Click); | |
} | |
); | |
}, | |
builder: (context, vm) { | |
return Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: new FloatingActionButton( | |
// Attach the `callback` to the `onPressed` attribute | |
onPressed: vm.onClick, | |
tooltip: 'Increment', | |
child: new Icon(Icons.exposure_plus_1), | |
), | |
); | |
}, | |
); | |
} | |
} | |
class AddAsyncButton extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<AppState, AppStateViewModel>( | |
converter: (store) { | |
return AppStateViewModel( | |
onClick: () { | |
store.dispatch(asyncIncrement()); | |
store.dispatch(Actions.Click); | |
} | |
); | |
}, | |
builder: (context, vm) { | |
return Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: new FloatingActionButton( | |
// Attach the `callback` to the `onPressed` attribute | |
onPressed: vm.onClick, | |
tooltip: 'Increment', | |
child: new Icon(Icons.slow_motion_video), | |
), | |
); | |
}, | |
); | |
} | |
} | |
class DesButton extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return StoreConnector<AppState, AppStateViewModel>( | |
converter: (store) { | |
return AppStateViewModel( | |
onClick: () { | |
store.dispatch(Actions.Decrement); | |
} | |
); | |
}, | |
builder: (context, vm) { | |
return Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: new FloatingActionButton( | |
// Attach the `callback` to the `onPressed` attribute | |
onPressed: vm.onClick, | |
tooltip: 'des', | |
child: new Icon(Icons.exposure_neg_1), | |
), | |
); | |
}, | |
); | |
} | |
} | |
class FlutterReduxApp extends StatelessWidget { | |
final Store<AppState> store; | |
final String title; | |
FlutterReduxApp({Key key, this.store, this.title}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return new StoreProvider<AppState>( | |
store: store, | |
child: MaterialApp( | |
theme: ThemeData.dark(), | |
title: title, | |
home: Scaffold( | |
appBar: AppBar( | |
title: new Text(title), | |
), | |
body: new Center( | |
child: new Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
new Text( | |
'You have pushed the button this many times:', | |
), | |
SimpleText(), | |
], | |
), | |
), | |
// Connect the Store to a FloatingActionButton. In this case, we'll | |
// use the Store to build a callback that with dispatch an Increment | |
// Action. | |
// | |
// Then, we'll pass this callback to the button's `onPressed` handler. | |
floatingActionButton: Row( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
AddButton(), | |
DesButton(), | |
AddAsyncButton(), | |
], | |
), | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
redux-combine:
redux-thunk: