Multi-Provider invalid member on null: 'collection' on provider construct - firebase

The error received:
NoSuchMethodError: invalid member on null: 'collection'
I'm using the Flutter Provider Package 4.3.2+2 for complex state management.
I get my data from Firebase, this is the data structure:
This is the provider class:
class ClientsProvider extends ChangeNotifier {
FirebaseFirestore _fs;
StreamSubscription<QuerySnapshot> _stream;
List<QueryDocumentSnapshot> clients = [];
ClientsProvider() {
_stream = _fs.collection("clients").snapshots().listen((event) {//THIS IS WHERE THE ERROR POINTS
clients = event.docs;
notifyListeners();
});
}
#override
void dispose() {
super.dispose();
_stream.cancel();
}
}
This is the parent widget:
class _ParentState extends State<Parent> {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<ClientsProvider>(
create: (context) => ClientsProvider()),
ChangeNotifierProvider<MyProvider>(
create: (context) => MyProvider()),
],
child: Dashboard(),//This widget contains the Child() widget two widgets down.
);
}
}
This is the child widget that needs to be updated when the firebase snapshot updates:
class _ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return Column(
children: [
_buildAddNew(),
Consumer<ClientsProvider>(
builder: (context, clientProvider, child) {
return Container(child: Text(clientProvider.clients.toString()));
},
),
],
);
}
}
Once again, the error received:
NoSuchMethodError: invalid member on null: 'collection'
The data isn't null, why am I receiving this error?

_fs is null because you never assigned it. Perhaps you meant to do this, as shown in the documentation:
FirebaseFirestore _fs = FirebaseFirestore.instance;

Related

Use stream provider inside Stateful Widget to get data for single firestore document

I have specific firestore document and I want get the value of field of that document updated every second because the field represent the number of notifications of user, But when I Looking for stream provider first time I was read we can't use it inside Stateful widget.
My Document Path
My home page code:
class Home extends StatefulWidget {
final User me;
Home({
Key key,
this.me,
}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final Firestore _firestore = Firestore.instance;
int numberOfNotifications;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: GlobalUniversal.whiteBG,
body: Center(
child: Text(numberOfNotifications.toString()),
),
);
}
}
db.dart
class DatabaseService {
final Firestore db = Firestore.instance;
Future<UserNotification> getUserNotification(String doc) async {
var snap = await db.collection(NOTIFICATIONS_USERS_ONE_COLLECTION).document(doc).get();
return UserNotification.fromMap(snap.data);
}
Stream<UserNotification> streamUserNotifications(String doc){
return db.collection(NOTIFICATIONS_USERS_ONE_COLLECTION).document(doc).snapshots().map((snap) => UserNotification.fromMap(snap.data));
}
}
user.dart
class UserNotification {
int userNotifications;
UserNotification({this.userNotifications});
factory UserNotification.fromMap(Map data) {
return UserNotification(
userNotifications: data[UN_READ_COUNTS_FIELD] ?? 0,
);
}
}
but when I try call provider inside home page I got an error.
error: The named parameter 'stream' isn't defined. (undefined_named_parameter)
Don't use the .value if you are instantiating your stream in your provider
StreamProvider<UserNofitication>(
create: (BuildContext context) => db.streamUserNotification(widget.me.doc),
child: Center(
child: Text(numberOfNotifications.toString()),
),
),
If db.streamUserNotification(widget.me.doc) is already an instance of stream, you can use StreamProvider.value and the named parameter is value and not stream :
StreamProvider<UserNofitication>.value(
value: db.streamUserNotification(widget.me.doc),
child: Center(
child: Text(numberOfNotifications.toString()),
),
),
EDIT
to use the stream you can use the builder of the StreamProvider to read the context
StreamProvider<UserNofitication>(
create: (BuildContext context) => db.streamUserNotification(widget.me.doc),
builder: (BuildContext context, Widget child) {
numberOfNotifications = context.watch<UserNotification>()?.userNotifications ?? 0;
return Center(
child: Text(numberOfNotifications.toString()),
);
},
),

Flutter - Listening for data changes in user profile (Firestore) of Firebase user using provider package

By now I successfully react on auth-changes of my Firebase user to map it to my own custom user class
import 'package:mypckg/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:mypckg/services/database.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
Future<local.User> _userFromFirebaseUser(auth.User user) async {
return user != null
? local.User(/* ... init user properties ... */)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().asyncMap(_userFromFirebaseUser);
}
...
In Cloud Firestore I store additional values of that user which are not covered by Firebase user e.g.
In main.dart the provider is set to provide my app with the local user in case he signed in or signed out (authStateChanges). My idea was to subscribe to another stream which will listen to changes on the 'users' document in Cloud Firestore.
class MyPckg extends StatelessWidget {
#override
Widget build(BuildContext context) {
final i18n = I18n.delegate;
//AuthService().signInAnon();
return MultiProvider(
providers: [
StreamProvider<User>(
create: (_) => AuthService().user,
),
/* my idea to subscribe to another stream which will listen to changes on user details in Firestore */
StreamProvider<User>(
create: (_) => AuthService().customUser,
),
],
child: DynamicTheme(
defaultBrightness: Brightness.light,
data: (brightness) {
...
I have a profile view where the user may edit those values, e.g. the locale and it gets written to the Firestore correctly with
Future<void> updateUserLanguage(String language) async {
return await usersCollection.doc(uid).update({
'language': language,
});
}
But the view is not rebuild as the current stream only reacts to authStateChanges.
Does anyone have a working example how to setup the link from users in Firestore collection that my app will listen to changes done there? What will my customUser method have to look like?
Thank you!
I've decided to wrap everything in my logged-in state with a StreamProvider:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if (user == null) {
return Authenticate();
} else {
return StreamProvider<Profile>.value(
value: DatabaseService(uid: user.uid).profile,
initialData: Profile(userName: '0', userRole: '0'),
child: HomeScreen(),
);
}
}
}
The db service takes in the UID and returns a snapshot:
class DatabaseService {
final String uid;
DatabaseService({
this.uid,
});
static CollectionReference _profileCollection =
FirebaseFirestore.instance.collection('profiles');
Stream<Profile> get profile {
return _profileCollection.doc(uid).snapshots().map(_profileFromSnapshot);
}
Profile _profileFromSnapshot(DocumentSnapshot snapshot) {
return Profile(
userName: snapshot.data()['userName'] ?? '',
userRole: snapshot.data()['role'] ?? '',
);
}
}
Finally, I use the data via a Consumer wherever I need it:
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Consumer<Profile>(builder: (
context,
profile,
child,
) {
return Text(profile.userName)
...
edit: I've made a video about this technique. Link: https://youtu.be/mAH9YT_y6ec
For those who are struggling with this approach as well, here is my solution (thanks to all contributors who lead me to that approach):
I set up a method "setAppUser" in MyClass which can be called from any child. Important for this
static _MyClassState? of(BuildContext context) =>
context.findAncestorStateOfType<_MyClassState>();
So here is main.dart (only the necessary sections):
class MyClass extends StatefulWidget {
#override
_MyClassState createState() => _MyClassState();
static _MyClassState? of(BuildContext context) =>
context.findAncestorStateOfType<_MyClassState>();
}
class _MyClassState extends State<MyClass> {
User appUser = User();
set setAppUser(User user) => {
setState(() {
appUser = user;
})
};
#override
Widget build(BuildContext context) {
...
return MultiProvider(
providers: [
StreamProvider<UserLogin>(
initialData: UserLogin(signedIn: false),
create: (_) => AuthService().userAuthStateChanges,
catchError: (_, error) => UserLogin(signedIn: false),
),
StreamProvider<User>(
initialData: appUser,
create: (_) => AuthService().userData,
catchError: (_, error) => appUser,
),
],
child: MaterialApp(
...
In my sign-in form I call the "setAppUser" method of my main.dart after receiving a new user of my sign-in. Now the widget gets rebuilt and the new appUser is set to my provider.
...
await _auth
.signInWithEmailAndPassword(
textControllerEmail.text,
textControllerPassword.text)
.then((result) {
if (result.id != "") {
MyClass.of(context)!.setAppUser = result;
...

Pass user uid to Firestore stream query in Flutter

Here I am using provider in main.dart to stream "itemsInUserDocument" data from a document.
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<DocumentSnapshot>.value(
value: DatabaseService().itemsInUserDocument, <--- (1) Providing here
),
StreamProvider<User>.value(
value: AuthProvider().user,
),
],
My problem is passing the userUID to the Stream query here. My document id's on Firestore is my userUID.
final userUID = "3SlUigYBgsNgzjU8a9GSimhAhuu1"; <-- (2) Need to pass to stream "userID' below??
class DatabaseService {
Stream<DocumentSnapshot> get itemsInUserDocument {
final DocumentReference userData = Firestore.instance.collection('userdata').document(userUID);
return userData.snapshots();
}
}
Here I am using the list in down the widget tree.
#override
Widget build(BuildContext context) {
final userTaskDone = Provider.of<DocumentSnapshot>(context);
List taskList = []; <-- (3) Using the list here.
taskList = userTaskDone['tasks'].toList();
print(taskList);
It currently works and I am getting the streamed data in section (3) as is now, but I need to pass the Firebase user UID into the stream in section (2).
I can get the user UID in a stateful widget with setState function but everything I have tried doesn't work or seems like I am duplicating things too much, I am trying to use Provider to manage state properly.
I can get the userUID with provider as well, but you can only use it on a (context), where my notifier section (2) is only a class.
PLEASE help me with a solution.
EDIT:
I have tried the edits and the result is as follow:
Getting this critical error from provider when I enter the screen where it used to work before these changes to main.dart.
Here is the full code for main.dart with edits as it is now:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: 'Sendr',
home: MainScreen(),
routes: {
SplashPage.id: (context) => SplashPage(),
LoginScreen.id: (context) => LoginScreen(),
NavigatorScreen.id: (context) => NavigatorScreen(),
CragSelectionScreen.id: (context) => CragSelectionScreen(),
CragRoutesScreen.id: (context) => CragRoutesScreen(),
RouteDetailScreen.id: (context) => RouteDetailScreen(),
MapScreen.id: (context) => MapScreen(),
},
);
}
}
///Authentication Logic
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthProvider().user,
child: Consumer<User>(
builder: (context, user, __) {
if (user == null) {
return LoginScreen();
} else {
return MultiProvider(
providers: [
Provider<DatabaseService>.value(
value: DatabaseService(user.uid),
),
],
child: NavigatorScreen(),
);
}
},
),
);
}
}
This is my Database Service:
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseService {
DatabaseService(this.uid);
final String uid;
Stream<DocumentSnapshot> get itemsInUserDocument {
final DocumentReference userData =
Firestore.instance.collection('userdata').document(uid);
return userData.snapshots();
}
}
I have made no change to my step (3) where I am using it, is this where my problem might be now?
I am trying to do more research on this. Working through the provider docs. If I understand your suggestion correctly, we are streaming the User value from AuthProvider at the top, then consume the User value just below it then passing the user.uid value to the DatabaseService, which is used down the widget tree again. Looks like I am almost there from your help. If you don't mind, please let me know what you think of the provider error on the screen. Much appreciated.
You could try a switchmap from rxdart which listens to and maps a stream T to one of S.
Stream<S> switchMap<S>(Stream<S> Function(T value) mapper) =>
transform(SwitchMapStreamTransformer<T, S>(mapper));
import 'package:rxdart/transformers.dart';
final userStream = AuthProvider().user;
final itemsStream = userStream.switchMap<DocumentSnapshot>(
(user) {
if (user == null) return Stream<DocumentSnapshot>.value(null);
return itemsCollection.where('uid', isEqualTo: user.uid).snapshots();
},
);
StreamProvider<DocumentSnapshot>.value(
value: itemsStream,
),
Writing this off the cuff so it might be wrong.
Edit
Another way would just be to place your items provider one level below your user. That way you can just regularly consume the user uid value.
return StreamProvider<User>.value(
value: AuthProvider().user,
child: Consumer<User>(
builder: (_, user, __) {
if (!user) {
// Not logged in
} else {
return MultiProvider(
providers: [
Provider.value<DatabaseService>(
value: DatabaseService(user.uid),
),
// More services that rely on user.uid
],
child: SizedBox(),
);
}
},
),
);
2 Edit
class DatabaseService {
DatabaseService(this.uid);
final String uid;
getData() {
return MyCollection.where('uid', isEqualTo: uid).snapshots();
}
}
This seems to have worked having main.dart looking as below:
Main.dart
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthProvider().user,
child: Consumer<User>(
builder: (context, user, __) {
if (user == null) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: 'Sendr',
home: LoginScreen(),
);
} else {
return MultiProvider(
providers: [
StreamProvider<DocumentSnapshot>.value(
value: DatabaseService(user.uid).userRoutesDone,
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: 'Sendr',
home: NavigatorScreen(),
routes: {
SplashPage.id: (context) => SplashPage(),
LoginScreen.id: (context) => LoginScreen(),
NavigatorScreen.id: (context) => NavigatorScreen(),
MapScreen.id: (context) => MapScreen(),
},
),
);
}
},
),
);
My Notifier:
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseService {
DatabaseService(this.uid);
final String uid;
Stream<DocumentSnapshot> get itemsInUserDocument {
final DocumentReference userData =
Firestore.instance.collection('userdata').document(uid);
return userData.snapshots();
}
}
Where I used it down the widget tree:
#override
Widget build(BuildContext context) {
final userTaskDone = Provider.of<DocumentSnapshot>(context);
List taskList = []; <-- (3) Using the list here.
taskList = userTaskDone['tasks'].toList();
print(taskList);

How to use a Pagview PageController in Flutter using Redux

I am using Redux within Flutter (and I am just starting to learn both). I have been trying to figure out how to switch between the pages of a PageView using the PageView's PageController.
However, whenever I try to use the PageController.jumpToPage() function, I get an exception stating:
"The following assertion was thrown while finalizing the widget tree: setState() or markNeedsBuild() called when widget tree was locked."
When I attempt to call the PageController.jumpToPage() in my reducer, it does navigate to the page within the pageview; but the exception gets thrown.
I have also tried just building a new PageController in the reducer, and just setting the PageController's initial page property to the desired page, but that didn't seem to do anything.
I have run out of ideas on how to figure this out on my own, so I thought I would ask here. Any help would be appreciated.
I have thrown together a quick sample showing what I am trying to do:
import 'package:flutter/material.dart';
import 'package:redux/redux.dart';
import 'package:flutter_redux/flutter_redux.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final store = Store<AppState>(appReducer,
initialState: AppState.initial(), middleware: []);
#override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: MaterialApp(
title: 'PageView Example With Redux',
home: MyPageViewContainer(),
),
);
}
}
class AppState {
final List<Widget> pageViewList;
final PageController pageController;
AppState({
this.pageViewList,
this.pageController,
});
factory AppState.initial() {
return AppState(
pageViewList: [
PageOne(),
PageTwo(),
],
pageController: PageController(initialPage: 0),
);
}
AppState copyWith({
List<Widget> pageViewList,
PageController pageController,
}) {
return AppState(
pageViewList: pageViewList ?? this.pageViewList,
pageController: pageController ?? this.pageController,
);
}
}
AppState appReducer(AppState state, action) {
if (action is NavigateToPageOneAction) {
state.pageController.jumpToPage(0);
return state;
}
else if (action is NavigateToPageTwoAction) {
state.pageController.jumpToPage(1);
return state;
}
else {
return state;
}
}
class NavigateToPageOneAction {}
class NavigateToPageTwoAction {}
class MyPageView extends StatelessWidget {
final List<Widget> pageViewList;
final PageController pageController;
final Function onPageChanged;
MyPageView({
this.pageViewList,
this.pageController,
this.onPageChanged,
});
#override
Widget build(BuildContext context) {
return PageView(
controller: pageController,
children: pageViewList,
onPageChanged: onPageChanged,
);
}
}
class MyPageViewContainer extends StatelessWidget {
MyPageViewContainer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _MyPageViewModel>(
converter: (Store<AppState> store) => _MyPageViewModel.create(store),
builder: (BuildContext context, _MyPageViewModel vm) {
return MyPageView(
pageViewList: vm.pageViewList,
pageController: vm.pageController,
);
},
);
}
}
class _MyPageViewModel {
final List<Widget> pageViewList;
final PageController pageController;
final Function onPageChanged;
_MyPageViewModel({
this.pageViewList,
this.pageController,
this.onPageChanged,
});
factory _MyPageViewModel.create(Store<AppState> store) {
_onPageChanged() {}
return _MyPageViewModel(
pageViewList: store.state.pageViewList,
pageController: store.state.pageController,
onPageChanged: _onPageChanged(),
);
}
}
class PageOne extends StatelessWidget {
PageOne();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page One"),
),
backgroundColor: Colors.black,
body: Column(),
drawer: MyDrawer(),
);
}
}
class PageTwo extends StatelessWidget {
PageTwo();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page Two"),
),
backgroundColor: Colors.blue,
body: Column(),
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
MyDrawer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _MyDrawerViewModel>(
converter: (Store<AppState> store) => _MyDrawerViewModel.create(store),
builder: (BuildContext context, _MyDrawerViewModel vm) {
return Drawer(
child: ListView(
children: <Widget>[
Container(
child: ListTile(
title: Text(vm.pageOneText),
onTap: vm.pageOneOnTap,
),
),
Container(
child: ListTile(
title: Text(vm.pageTwoText),
onTap: vm.pageTwoOnTap,
),
),
],
),
);
},
);
}
}
class _MyDrawerViewModel {
final String pageOneText;
final String pageTwoText;
final Function pageOneOnTap;
final Function pageTwoOnTap;
_MyDrawerViewModel({
this.pageOneText,
this.pageTwoText,
this.pageOneOnTap,
this.pageTwoOnTap,
});
factory _MyDrawerViewModel.create(Store<AppState> store) {
_goToPageOne() {
store.dispatch(NavigateToPageOneAction());
}
_goToPageTwo() {
store.dispatch(NavigateToPageTwoAction());
}
return _MyDrawerViewModel(
pageOneText: "Page One",
pageTwoText: "Page Two",
pageOneOnTap: _goToPageOne,
pageTwoOnTap: _goToPageTwo,
);
}
}
I seem to have figured out how to solve my problem. I saw an answer in this post: Flutter: setState() or markNeedsBuild() called when widget tree was locked... during orientation change
In that post the OP was encountering the same error when changing between portrait and landscape mode while the Drawer was open. The answer in that post suggested calling Navigator.pop() (which closes the Drawer) before changing view modes.
So I gave that a try and closed my Drawer using the Navigator.pop() prior to using the PageController's .jumpToPage method. This seems to work, and allows me to navigate between pages of the PageView using onTap events from the Drawer, without throwing the "The following assertion was thrown while finalizing the widget tree: setState() or markNeedsBuild() called when widget tree was locked" exception.
I assume that this means that while the Drawer is open, the widget tree is placed into a locked state.
Hopefully this helps someone, as it took me a while to figure out.
#Blau
Sometimes an event happened outside any widgets you built. e.g. (1) A timer that increment a 'Global Counter', this counter will be shown in many pages/widgets (2) A message sent from the socket server, on receiving this message/event, the user may be anywhere(any pages/widgets), and you don't know where to 'setState' (Or the widget is actually not there because the user is not at that page)
I've built 2 examples that demonstrate how to use Redux to solve this kind of problems:
Example 1: (Use a multi-thread timer to 'setState' a widget when an external event fires)
https://github.com/lhcdims/statemanagement01
Example 2: (Use Redux to refresh a widget when an external event fires)
https://github.com/lhcdims/statemanagement02
Demo Screen Shot:

Navigate to a new screen and passing the redux store state when using a viewmodel

I have the need to pass the redux store state from a screen to another inside the build function.
My problem is that in the build function I have the ViewModel variable, that does not have a reference to the state.
This is the code of the screen:
import ...
class Menu extends StatefulWidget {
#override
_MenuState createState() => _MenuState();
}
class _MenuState extends State<Menu> {
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, ViewModelLogin>(
converter: (store) => ViewModelLogin.create(store),
builder: (context, ViewModelLogin viewModel) {
Widget _buildPage(isLoggedIn) {
if (isLoggedIn) {
return ListView(
children: <Widget>[
ListTile(
title: Text('Settings'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MySettingsScreen(), // <-- HERE I NEED TO PASS THE STATE TO MySettingsScreen
),
);
},
),
ListTile(
leading: Image.network(
viewModel.loginType == 'facebook'
? 'https://img.icons8.com/color/52/000000/facebook.png'
: 'https://image.flaticon.com/teams/slug/google.jpg'
,
width: 30.0,
),
title: Text('Exit'),
onTap: () {
viewModel.onLogout(viewModel.loginType);
}
),
],
);
} else {
return LoginScreen(appBar: false);
}
}
return _buildPage(viewModel.isLoggedIn);
},
);
}
}
The reason I need to pass the state to MySettingsScreen is that in the screen I need a store variable to do a get call to a webservice (outside the build function).
This is a part of MySettingsScreen where I need the store state:
import ...
class MySettingsScreen extends StatefulWidget {
final AppState state;
MySettingsScreen({Key key, #required this.state}) : super(key: key);
#override
_MySettingsScreenState createState() => _MySettingsScreenState();
}
class _MySettingsScreenState extends State<MySettingsScreen> {
#override
void initState() {
super.initState();
_load();
}
void _load() async {
final url = 'url';
try {
http.Response res = await http.get(url, headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + // <-- HERE I NEED THE STORE VARIABLE
});
final data = json.decode(res.body);
tmpItems = _parseItems(data["bookings"]);
} catch(e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
...
}
}
For the most common use case of an app wide state you can just use the StoreConnector in any screen where you need the state. You are already using it in your Menu class, but you can use it similarly in your MySettingsScreen build method override.
Additionally your view models are being created using the store, so the idea is that you include all information that is required from the store to build the view when you build the view model. For example:
class MyViewModel {
String myInfo;
factory MyViewModel.create(Store<AppState> store) {
return MyViewModel(myInfo: store.state.myInfoState);
}
...
}
Then you use that information from your viewmodel:
#override
Widget build(BuildContext context) => StoreConnector<AppState, MyViewModel>(
converter: (Store<AppState> store) => MyViewModel.create(store),
builder: (BuildContext context, MyViewModel viewModel) {
return Text(viewModel.myInfo);
}
You could use the same mechanism to keep a reference to the store in the viewModel:
class MyViewModel {
Store<AppState> myStore;
factory MyViewModel.create(Store<AppState> store) {
return MyViewModel(myStore: store);
}
...
}
This allows you to use it in your build method directly:
#override
Widget build(BuildContext context) => StoreConnector<AppState, MyViewModel>(
converter: (Store<AppState> store) => MyViewModel.create(store),
builder: (BuildContext context, MyViewModel viewModel) {
return Text(viewModel.myStore.state.myInfo);
}
Note that the first pattern may be preferable if you want to keep a logical separation between the view model and the redux persistence.

Resources