This paper is relatively slag, to be reconstructed....
Today’s task is to redux yesterday’s code.
Before that, it is said to unify several nouns in this article. See github for the source of this article
-Leslie: Store dispatch action reducer connector provider to Converter Builder dependency Flutter_redux: ^ 0.5.3Copy the code
1. Redux the initial project
Everyone should still remember the initial project, the following is its carding diagram, sharpening the knife is not wrong to cut wood workers.
I’m going to start with that and give you a quick overview of what redux is.
1.1: Analyze behavior and change
Quite simply, the behavior is the click, and the change is the growth of the number.
Reducer is not pure or impure. In my opinion, it is an independent logical unit that can exist logically without relying on the outside world: given an input, an expected output will be returned
Enum Actions {increment// Define add Actions} // Use counterReducer to reduce Actions from class. Int counterReducer(int input, dynamic action) {var output; switch(action){ case Actions.increment: output=input+1; break; } return output; }Copy the code
1.2: Create the ReduxPage component
One of the cores of Redux is the Store, which is a repository for storing, supplying and distributing.
Returns a repository provider, which is a Widget that requires the Store and Child attributes.
class ReduxPage extends StatelessWidget {
final Store<int> store;
ReduxPage({Key key, this.store,}) : super(key: key);
return StoreProvider<int>(
store: store,
child: child,
);
}
Copy the code
1.3: Now the focus is on how children are constructed
For clarity, separate countText and Fab from the state-related components
var child = MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: Text("Flutter Redux Demo"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [Text('You have pushed the button this many times'), countText// show digit Text],),), floatingActionButton: Fab,// click the button),);Copy the code
Text:countText to display the number
If you want to get something from the warehouse, what do you need? The key to bai. The StoreConnector warehouse connector is this key
The Converter calls back the Store object, which you can then value through the store and return through the constructor
Var countText= StoreConnector<int, String>(Converter: (store) => store.state.toString(), (context, count) {// Constructor, build Widget return Text(count, style: theme.of (context).textTheme.display1,); });Copy the code
Buttons that handle actions
The processing action also requires a warehouse, which is used to dispatch corresponding actions.
In the constructor, you can use the action logic.
var fab= StoreConnector<int, VoidCallback>( converter: (store) { return () => store.dispatch(Actions.increment); }, Builder: (Context, callback) {return FloatingActionButton(onPressed: callback, Tooltip: 'Increment', child: Icon(Icons.add), ); });Copy the code
Here, the action side is the attack side and the response side is the receiver side. The figure below illustrates how the two widgets are built
1.4: Construction of warehouse objects
So the core of this is the repository store, so how do you generate objects
void main() {
final store = Store<int>(counterReducer, initialState: 0);
runApp(ReduxPage(
store: store,
));
}
Copy the code
2. The story
You might say, “It doesn’t feel good either. It feels like a hassle.”
2.1: When adding a feature
What if I want to add 10? With Redux you need to define a behavior and a response.
Modify the behavior when the behavior is distributed. Maybe you said I don’t need redux, just change careers. What if there’s a lot of logic and then you have to change it back? It is very convenient to abstract out a behavior to manage the logic switch and the modification can be done directly in the Reducer, avoiding contamination of the packaged component source code.
enum Actions{
increment,
increment10
}
int counterReducer(int input, dynamic action) {
var output;
switch(action){
case Actions.increment:
output=input+1;
break;
case Actions.increment10:
output=input+10;
break;
}
return output;
}
var fab= StoreConnector<int, VoidCallback>(
converter: (store) {
return () => store.dispatch(Actions.increment10);
},
Copy the code
2.2: Global state sharing
How easily another interface can enjoy the data of the previous interface is a big problem.
Of course, you can pass parameters by constructing, but this is obviously messy and takes in parameters.
import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; class SecondPage extends StatelessWidget { SecondPage({Key key}) : super(key: key); @override Widget build(BuildContext context) {var text = StoreConnector<int, String> (store) => store.state.toString(), builder: (context, count) { return Text( count.toString(), style: Theme.of(context).textTheme.display1, ); }); return Scaffold( appBar: AppBar( title: Text("SecondPage"), ), body: Align( alignment: Alignment.topCenter, child: text, ), ); }}Copy the code
Add text to ReduxPage by clicking to jump to SecondPage
Builder _skipToSecondPage(StoreConnector<int, String> countText) { return Builder( builder: (context) => InkWell(child: countText, onTap: () { Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return SecondPage(); })); },),); }Copy the code
3. A remake of yesterday’s TodoList
It’s the same interface.
3.1: Define the Todo description class
class Todo { String sth; Bool done; // Todo({this.sth, this.done}); // Is that done?}Copy the code
3.2: Define state classes and actions and changes
Yesterday we analyzed one with three states and four actions
class TodoState { List<Todo> todos; // List data String text; ShowType ShowType; TodoState({this.todos, this.text, this.showType}); // display type} Acts {add, // add to todo selectAll, // selectAll selectTodo, // selectAll selectDone, } TodoState todoReducer(TodoState input, dynamic action) {switch (action) {case Acts. Add: if (input.text! = null && input.text ! = "") { input.todos.add(Todo(sth: input.text, done: false)); input.text = ""; } break; case Acts.selectAll: input.showType=ShowType.all; break; case Acts.selectTodo: input.showType=ShowType.todo; break; case Acts.selectDone: input.showType=ShowType.done; break; } return input; } final todoListStore = Store<TodoState>(todoReducer, initialState:// initialState) "", showType: ShowType.all));Copy the code
3.3: Component encapsulation
As above, use StoreConnector to get the resource from the repository, except that the resource is a TodoState object
If you move, as the attacker, you still call back to perform the corresponding move.
class TodoList extends StatefulWidget { final Store<TodoState> store; TodoList({ Key key, this.store, }) : super(key: key); @override _TodoListState createState() => _TodoListState(); } class _TodoListState extends State<TodoList> { @override Widget build(BuildContext context) { var textField= StoreConnector<TodoState, TodoState>(Converter: (store) =>store.state,// (context, state) {// Constructor, build Widget return TextField(Controller: TextEditingController(text: state.text), keyboardType: TextInputType.text, textAlign: TextAlign.start, maxLines: 1, cursorColor: Colors.black, cursorWidth: 3, style: TextStyle( fontSize: 16, color: Colors.lightBlue, backgroundColor: Colors.white), decoration: InputDecoration( filled: True, fillColor: colors. white, hintText: 'Add a todo item ', hintStyle: TextStyle(color: colors. black26, fontSize: ContentPadding: EdgeInsets. Only (left: 14.0, bottom: 8.0, top: 8.0), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.only( topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)), ), enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.white), borderRadius: BorderRadius.only( topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)), ), ), onChanged: (str){ state.text=str; }); }); var btn = StoreConnector<TodoState, VoidCallback>( converter:(store) { return () => store.dispatch(Acts.add); }, Builder: (context, callback) {return RaisedButton(child: Icon(icon.add), padding: EdgeInsets.zero, onPressed: (){ callback(); FocusScope.of(context).requestFocus(FocusNode()); }); }); var inputBtn = Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Container( child: textField, width: 200, ), ClipRRect( borderRadius: BorderRadius.only( topRight: Radius.circular(10), bottomRight: Radius.circular(10)), child: Container( child: btn, width: 36, height: 36, ), ), ], ); Var listInfo = [[" all ", Acts. SelectAll], [" done ", Acts. SelectTodo],]; Var op = Row (. / / operation button mainAxisAlignment: mainAxisAlignment spaceEvenly, children: listInfo.map((e) { return StoreConnector<TodoState, VoidCallback>( converter: (store) { return () => store.dispatch(e[1]); }, builder: (context, callback) { return RaisedButton( onPressed: callback, child: Text(e[0]), color: Colors.blue, ); }); }).toList(), ); Var listView = StoreConnector<TodoState, TodoState>(Converter: (store) => store.state, // (context, state) { var result; // Constructor, build Widget switch(state.showtype){case showType. All: result= formList(state.todos); break; case ShowType.todo: result= formList(List.of( state.todos.where((e)=>! e.done))); break; case ShowType.done: result= formList(List.of( state.todos.where((e)=>e.done))); break; } return result; }); return StoreProvider<TodoState>( store: widget.store, child: Column( children: <Widget>[inputBtn, op, Expanded(child: listView)], ), ); } Widget formList(List<Todo> todos) { return ListView.builder( itemCount: todos.length, padding: EdgeInsets. All (8.0), itemExtent: 50.0, itemBuilder: (BuildContext context, int index) {var key = todos[index]. var value = todos[index].done; var text = Align( child: Text( key, style: TextStyle( decorationThickness: 3, decoration: value ? TextDecoration.lineThrough : TextDecoration.none, decorationColor: Colors.blue), ), alignment: Alignment.centerLeft, ); return Card( child: Row( children: <Widget>[ Checkbox( onChanged: (b) { todos[index].done = b; setState(() {}); }, value: todos[index].done, ), text ], ), ); }); }}Copy the code
3.3: Use of ViewModel
You can see that there are two generics in StoreConnector, the second of which is named ViewModel
Now it is very convenient to implement that the CheckBox is hidden after the completed and incomplete buttons are clicked.
-->[1. Add a state]---- class TodoState {List<Todo> todos; // List data String text; ShowType ShowType; // Display type bool showBox=true; / / display the checkBox TodoState ({this. Todos, enclosing text, enclosing showType, enclosing showBox}); ---- case Acts. SelectAll: input.showBox=true; input.showType=ShowType.all; break; case Acts.selectTodo: input.showType=ShowType.todo; input.showBox=false; break; case Acts.selectDone: input.showType=ShowType.done; input.showBox=false; break; ----> ---- var checkBox=StoreConnector<TodoState, CheckBoxViewModel>(Converter: (store) { return CheckBoxViewModel(store,index); }, builder: (context, model) { return Offstage(child:Checkbox( value: model.done, onChanged: model.onClick) , offstage: ! model.store.state.showBox,) ; }); ---->[define CheckBox model]---- class CheckBoxViewModel {final Store Store; final int index; void Function(bool check) onClick; CheckBoxViewModel(this.store,this.index) { onClick = (check) { store.dispatch(Acts.check); store.state.todos[index].done = check; }; } get done{ return store.state.todos[index].done; }}Copy the code
This makes it easy to make changes to the state that would otherwise be difficult to do directly in TodoList.
conclusion
This is the end of this article. If you want to taste Flutter quickly, Flutter For Seven days is a must-have. If you want to explore it, follow in my footsteps and complete a Flutter tour. In addition, I have a Flutter wechat communication group. You are welcome to join and discuss Flutter issues together. My wechat account is ZDL1994328.