I'm using redux and redux_sage work for my flutter app.
my_store.dart
Store<AppState> myStore() {
var sagaMiddleware = createSagaMiddleware();
AppState appReducer(AppState state, action) {
return AppState(
user: userReducer(state.user, action),
);
}
final store = Store<AppState>(
appReducer,
initialState: AppState.initial(),
middleware: [applyMiddleware(sagaMiddleware)],
);
sagaMiddleware.setStore(store);
sagaMiddleware.run(rootSaga);
return store;
}
saga.dart
...
login({action}) sync* {
var user = Result<User>();
yield Call(loginAPI, args: [action.auth], result: user);
yield Put(LoginSuccessAction(user.value!));
}
watchSaga() sync* {
yield TakeLatest(login, pattern: LoginAction);
//yield TakeEvery(fetchUser, pattern: FetchUserAction);
}
rootSaga() sync* {
yield All({
#t1: Fork(watchSaga),
});
}
action.dart
class LoginAction {
Auth auth;
LoginAction(this.auth);
}
class LoginSuccessAction {
User user;
LoginSuccessAction(this.user);
}
reducers/user.dart
final userReducer = combineReducers<User>([
TypedReducer<User, FetchUserAction>(fetchUserReducer),
TypedReducer<User, LoginSuccessAction>(loginSuccessReducer),
]);
User fetchUserReducer(User state, FetchUserAction action) {
return state.copyWith(fullname: state.fullname);
}
User loginSuccessReducer(User state, LoginSuccessAction action) {
//here can get the user data, but doesn't update later.
return action.user;
}
main.dart
void main() async {
runApp(MainApp(store: myStore()));
}
class MainApp extends StatelessWidget {
final Store<AppState> store;
const MainApp({super.key, required this.store});
@override
Widget build(BuildContext context) {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
return StoreProvider<AppState>(
store: store,
child: MaterialApp(
title: 'Provider Demo',
navigatorKey: navigatorKey,
onGenerateRoute: (RouteSettings settings) {
final Map<String, WidgetBuilder> routes = {
'/': (context) => const HomePage(),
'/login': (context) => const Login(),
};
WidgetBuilder builder;
if (settings.name == '/login') {
builder = (context) => const Login();
} else {
User user = store.state.user;
debugPrint(user.toString());//when login, still get the old state
if (user.username == '') {
builder = (context) => const Login();
} else {
builder = routes[settings.name!] ?? (context) => const NotFound();
}
}
return MaterialPageRoute(builder: builder, settings: settings);
},
),
);
}
}
login.dart has a login button to dispatch LoginAction
onPressed: () async {
Auth auth = Auth(
username: _username,
password: _password,
);
await store.dispatch(LoginAction(auth));
Navigator.pushNamed(context, '/');
},
in saga.dart, Call(loginAPI, args: [action.auth], result: user) has returned the right data, but Put method doesn't work, it doesn't update the state, so I still get the old state. anything wrong?
last update: this bug is caused by async call, if I change the onPress code to:
onPressed: () async {
Auth auth = Auth(
username: _username,
password: _password,
);
await store.dispatch(LoginAction(auth));
await Future<void>.delayed(const Duration(seconds: 1));
Navigator.pushNamed(context, '/');
},
it works, so how to fix it?