I am using a provider to update my widget, but it doesn't update when I switch tab. Am I using it wrongly?
charts.dart
Widget chart(BuildContext context, ChartType chartType) {
return FutureBuilder<void>(
future: Provider.of<ChartModel>(context, listen: false).loadChartHabits(),
builder: (context, data) => Container(
Text(data);
);
);
}
class Charts extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
<Tab>[
Tab(text: Constants.weekly),
Tab(text: Constants.monthly),
],
),
title: Text('Tabs Demo'),
),
body: TabBarView(
children: [
chart(context, ChartType.Weekly),
chart(context, ChartType.Monthly),
],
),
),
),
);
}
}
I also did add this in main.dart
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => TodayModel()),
ChangeNotifierProvider(create: (_) => ChartModel()),
],
Update: It works after I placed a loadchart but is this the correct way to load data than needs await? I need to wait for the data to come back.
appBar: TabBar(
tabs: <Tab>[
Tab(text: Constants.weekly),
Tab(text: Constants.monthly),
],
onTap: (value) {
Provider.of<ChartModel>(context, listen: false).loadChart(ChartType.values[value]);
},
),
instead of FutureBuilder you should use Consumer
return Consumer<ChartModel>(
builder: (_, provider, __) {
return Container(child:Text(provider.data));
},
);
I am not sure that this will fit your particular case, because mine was a little bit m ore complicated. I solved by adding a state to the FutureBuilder, so, while changing tabs and setting the state, it would also update the state of the widgets inside the tabs. I'm not sure that the explanation is correct, but it did work in my case.
You should try changing your code like this:
Widget chart(BuildContext context, ChartType chartType) {
return StatefulBuilder(
builder: (context, setState) {
return FutureBuilder(
future: Provider.of<ChartModel>(context, listen: false).loadChartHabits(),
builder: (context, data) => Container(Text(data));
);
}
);
}
Related
I am using flutter screenutil and The following code used to work, but it gives me this error now.
The method 'setContext' isn't defined for the type 'ScreenUtil'. Try correcting the name to the name of an existing method, or defining a method named 'setContext'.
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) {
return themeChanger;
},
),
ChangeNotifierProvider(
create: (_) {
return settingChanger;
},
),
ChangeNotifierProvider(create: (_) {
return locationChanger;
})
],
child: Consumer2<DarkThemeProvider, AdvancedSettingsProvider>(
builder: (context, value1, value2, child) {
return ScreenUtilInit(
designSize: Size(1080, 2160),
builder: (_) => MaterialApp(
builder: (context, widget) {
ScreenUtil.setContext(context);
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1),
child: widget!);
},
theme: Styles.themeData(
themeChanger.darkTheme, context, themeChanger.color),
debugShowCheckedModeBanner: false,
home: (widget.payload.isEmpty)
? Skeleton()
: AccessedByNotifPage(
payload: widget.payload,
),
),
);
}),
);
}
}```
[![This image shows the error](https://imgur.com/a/l9B5fDJ)
Use init() function instead of setContext()
it's in the new update.
example:
builder: (context, widget) {
ScreenUtil.init(context);
}
You need to upgrade screenUtil version in pubsic.yaml to
flutter_screenutil: ^5.4.0
then flutter clean, flutter pub get and put this code in material app
builder: (ctx, child) {
ScreenUtil.setContext(ctx);
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: child!,
);
}
I am developing a chat app and all was fine when i was using only a stream provider which takes user id stream from firebase, but as i want real time changes when i add a chat, so i added multi provider, and gives it stream provider and change notfier provider, now both are not working, i have to hot restart the app for changes.
Widget build(BuildContext context) {
return MultiProvider(
providers: [
// authentication provider
StreamProvider<User?>(
create: (context) => AuthController().userStream(),
initialData: null,
),
//states provider
ChangeNotifierProvider(create: (context) => Cloud()),
],
builder: (context, _) => MaterialApp(
theme: ThemeData(
primaryColor: Colors.deepPurpleAccent,
textTheme: TextTheme(button: TextStyle(color: Colors.white)),
primarySwatch: Colors.deepPurple),
home: Scaffold(
body: Wrapper(),
),
));
}
You can Simply Use SteamBuilder & Firestore :
#override Widget build(BuildContext context) {
var streamBuilder = StreamBuilder<List<Message>>(
stream: getData(),
builder: (BuildContext context, AsyncSnapshot<List<Message>> messagesSnapshot) {
if (messagesSnapshot.hasError)
return new Text('Error: ${messagesSnapshot.error}');
switch (messagesSnapshot.connectionState) {
case ConnectionState.waiting: return new Text("Loading...");
default:
return new ListView(
children: messagesSnapshot.data.map((Message msg) {
return new ListTile(
title: new Text(msg.message),
subtitle: new Text(DateTime.fromMillisecondsSinceEpoch(msg.timestamp).toString()
+"\n"+(msg.user ?? msg.uid)),
);
}).toList()
);
}
}
);
return streamBuilder; }
here im try to use FutureBuilder for my list but I can't refresh by on pullRefresh
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _refreshPhotos, // fatch snapshot.data!
child: FutureBuilder<String>(
future: userId as Future<String>,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return LayoutBuilder(builder: (context, constraints) {
return ListView(
scrollDirection: Axis.vertical,
children: [
AddBanners(userId: snapshot.data!), // future builder,it fatches data from api
DealsOfTheDay(userId: snapshot.data!), //future builder, , it fatches data from api
]);
});
} else {
return Center(child: JumpingText('Loading...'));
}
}),
);
I want fresh these widgets along with
refreshPhotos()
AddBanners(userId: snapshot.data!),
DealsOfTheDay(userId: snapshot.data!)
If you are looking for pull to refresh. Wrap your widgets with 'RefreshIndicator' widget on your desired screen.
Here is an example of my home screen which has pull to refresh.
#override
Widget build(BuildContext context) {
return Scaffold(
key: _con.scafoldKey,
body: WillPopScope(
onWillPop:() => DeviceUtils.instance.onWillPop(),
child: SafeArea(
child: Container(
color: ColorUtils.themeColor,
child: RefreshIndicator( //Just add this to your screen
color: ColorUtils.themeColor,
key: _con.refreshIndicatorKey,
strokeWidth: 4,
displacement: 80,
onRefresh: _refresh, //this is a function which you need to place under your home view state
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
child: Container(
color: Colors.white,
child: /// some more widgets
),
),
),
),
);
}
After adding the refresh indicator to your widgets, you need to add the _refresh function which will have all your api's that you want to reload.
Future<Null> _refresh() async{
//these two are my api's that i want to reload everytime an user pulls to refresh screen. You have to add your own apis here.
_con.getProfile(context);
_con.getUpcoming(context);
}
Voila. Now your user can reload data in the page and get the new state.
Hope this answers your question.
If the above is not what you want. You can use setState() inside your future builder. See the code below for example:
class _MyHomePageState extends State<MyHomePage> {
Future<List<String>> _myData = _getData(); //<== (1) here is your Future
#override
Widget build(BuildContext context) {
var futureBuilder = new FutureBuilder(
future: _myData; //<== (2) here you provide the variable (as a future)
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new Text('loading...');
default:
if (snapshot.hasError)
return Column(
children: [
Icon(Icons.error),
Text('Failed to fetch data.'),
RaisedButton(
child: Text('RETRY'),
onPressed: (){
setState(){
_myData = _getData(); //<== (3) that will trigger the UI to rebuild an run the Future again
}
},
),
],
);
else
return createListView(context, snapshot);
}
},
);
return new Scaffold(
appBar: new AppBar(
title: new Text("Home Page"),
),
body: futureBuilder,
);
}
setState() will rebuild the widget with new values.
you can simply use in your main screen
setState((){});
it will rebuild all of the futureBuilder widgets in your screen and retrieve new data
I'm using a custom route to create a transition effect but I'm having some problems with a
value using Provider.
Error
Another exception was thrown: Error: Could not find the correct Provider<List<FireFavorites>> above this Favorites Widget
home.dart
return MultiProvider(
providers: [
StreamProvider.value(
value: db.streamFavorites(user.uid),
)
],
child: Scaffold(
body: StreamBuilder<NavBarItem>(
stream: _bottomNavBarBloc.itemStream,
initialData: _bottomNavBarBloc.defaultItem,
builder: (BuildContext context, AsyncSnapshot<NavBarItem> snapshot) {
switch (snapshot.data) {
case NavBarItem.SEARCH:
return Search();
case NavBarItem.BROWSE:
return Browse();
case NavBarItem.ICON:
return About();
case NavBarItem.FAVORITES:
return Favorites();
case NavBarItem.SETTINGS:
return Settings();
default:
return null;
}
},
),
bottomNavigationBar: ConvexBottomNav(navBarHandler: _navBarHandler),
),
);
}
custom route class
class MyCustomPageRoute extends MaterialPageRoute {
final Widget previousPage;
MyCustomPageRoute(
{this.previousPage, WidgetBuilder builder, RouteSettings settings})
: super(builder: builder, settings: settings);
#override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget currentPage) {
Animation<Offset> _slideAnimationPage1 =
Tween<Offset>(begin: Offset(0.0, 0.0), end: Offset(-1.0, 0.0))
.animate(animation);
Animation<Offset> _slideAnimationPage2 =
Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0))
.animate(animation);
return Stack(
children: <Widget>[
SlideTransition(position: _slideAnimationPage1, child: previousPage),
SlideTransition(position: _slideAnimationPage2, child: currentPage),
],
);
}
}
using the class like this which is in Browse(). I simply want to navigate to a document. But using MyCustomPageRoute causes the Provider error.
Navigator.push(
context,
MyCustomPageRoute(
previousPage: this,
builder: (context) => Document(
documentID: docid,
),
),
);
I would like to have the favorites StreamProvider in main.dart, but I don't have access to the authenticated user's uid there, or I don't know how to access it after it becomes available. Below is how I have Provider set up in main.dart. I can just make a simple request in home.dart, get the favorites and add them to Content() and the access them throughout the app, but I would like to have a stream.
main.dart
return MultiProvider(
providers: [
StreamProvider<FirebaseUser>.value(
value: FirebaseAuth.instance.onAuthStateChanged,
),
ChangeNotifierProvider<Content>.value(
value: Content(),
),
],
child: MaterialApp(
title: '',
theme: ThemeData(
fontFamily: 'OpenSans',
brightness: Brightness.light,
primaryColor: Color(0xff2398C3),
),
home: WelcomePage(),
),
);
Any idea how can this be fixed?
I have a multi provider config and I pass the providers in the mainApp and using the consumerProvider later. But I get the ancestor not found error. The same setup is working for another view but creating problems maybe because of the navigation
I have tried out some options that I found for similar problems in stackoverflow which stated moving the providers across and also looking at the context that is provided but did not find any solutions
First is my Provider.dart file
List<SingleChildCloneableWidget> providers = [
...independentServices,
...dependentServices,
];
List<SingleChildCloneableWidget> independentServices = [
Provider.value(value: FirebaseNewsService()),
Provider.value(value: FirebaseEventsService())
];
List<SingleChildCloneableWidget> dependentServices = [
ProxyProvider<FirebaseNewsService, NewsListModel>(
builder: (context, newsService, _) {
return NewsListModel(newsService: newsService);
}),
ProxyProvider<FirebaseNewsService, NewsCreateModel>(
builder: (context, newsService, _) {
return NewsCreateModel(newsService: newsService);
},
),
ProxyProvider<FirebaseEventsService, EventsListModel>(
builder: (context, eventsService, _) {
return EventsListModel(eventsService: eventsService);
}),
];
Next is the main.dart file
class MainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: providers,
child:MaterialApp(
title: 'MyApp',
initialRoute: RoutePaths.Home,
onGenerateRoute: Router.generateRoute, )
);
}
}
Next is the router.dart file where routing happens
class Router {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
// this is working
case RoutePaths.Home:
return MaterialPageRoute(builder: (context) {
NewsListModel model = Provider.of(context);
return ChangeNotifierProvider<NewsListModel>.value(
value: model, child: NewsPage());
});
break;
case RoutePaths.Events:
return MaterialPageRoute(builder: (_) {
EventListModel model = Provider.of(context);
return ChangeNotifierProvider<EventListModel>.value(
value: model, child: EventsListPage());
});
break;
My homepage file
class NewsPage extends StatelessWidget {
final String _tab1title = allTranslations.text('newsPage.tabtitleone');
final String _tab2title = allTranslations.text('newsPage.tabtitletwo');
static const _tablength = 2;
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tablength,
child: Scaffold(
drawer: Menu(), //Maybe Menu is having a different context
body: NestedScrollView(
...
body: Tabbarview(children: [] . // this works fine
In the problem widget the Events List .dart file
class EventListPage extends statelessWidget {
Widget build(BuildContext context) {
EventListModel model = Provider.of(context);
return Scaffold(drawer: Menu(), appBar: AppBar(), body: ChangeNotifierProvider<EventsListModel>.value(
value: model,
child: Consumer<EventsListModel>(
builder: (context, model, child) => model.busy
? Center(
child: CircularProgressIndicator(),
)
: Column(mainAxisSize: MainAxisSize.max, children: <Widget>[
SmartRefresher(
//key: EventsPageModel.eventsFollowKey,
controller: model.refreshController,
enablePullDown: true,
header: WaterDropMaterialHeader(
backgroundColor: Theme.of(context).primaryColor,
),
enablePullUp: true,
onRefresh: model.onRefresh,
onLoading: model.onLoading,
child: buildchild(model, context)),
]),
),
);
}
I always get could not find ancestor of consumer or
could not find the correct provider .Where I am doing it wrong.
the same thing works for the NewsListModel
Was a case sensitive import bug https://github.com/microsoft/TypeScript/issues/21736 .closing this question