I'm starting to learn Flutter and I'm trying to use provider.
I've follow a few tutorials and now starting my side project but I'm stuck with these error:
Error: Could not find the correct Provider<CultivatorModel> above this CultivatorComponent Widget
As I wrapped the MaterialApp with the Multiproviders, it should be in the ancestor... That error does not make sense to me, I'm certainly to new to flutter and I'm missing something obvious...
Anyway, here is my code, thanks for your help.
main.dart
import 'package:provider/provider.dart';
import 'package:flutter_app/components/cultivator_component.dart';
import 'package:flutter_app/models/incarnation_model.dart';
import 'package:flutter_app/models/cultivator_model.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(builder: (context) => IncarnationModel()),
ChangeNotifierProvider(builder: (context) => CultivatorModel()),
],
child: GameApp())
);
}
class GameApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CultivatorComponent(),
);
}
}
cultivator_component.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_app/Models/cultivator_model.dart';
class CultivatorComponent extends StatelessWidget{
#override
Widget build(BuildContext context) {
final cultivatorModel = Provider.of<CultivatorModel>(context);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('${cultivatorModel.cultivator.name} is here')
],
),
);
}
}
cultivator_model
import 'package:flutter/foundation.dart';
import 'package:flutter_app/domain/cultivator.dart';
import 'package:flutter_app/domain/incarnation.dart';
class CultivatorModel with ChangeNotifier{
Cultivator _cultivator;
Cultivator get cultivator => _cultivator;
CultivatorModel(){
_cultivator = Cultivator("MyCultivator");
}
bool canCreateIncarnation() => _cultivator.canCreateIncarnation();
void addIncarnation(Incarnation incarnation){
_cultivator.incarnations.add(incarnation);
notifyListeners();
}
}
Here is how my version works, I didn't do any major changes and my configuration is IntelliJ Idea, Flutter 1.9.1+hotfix.6 • channel stable and iOS virtual device
If you would have any problem with running the next code, please contact me and I try to help.
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(builder: (context) => AnotherModel()),
ChangeNotifierProvider(builder: (context) => CultivatorModel()),
],
child: GameApp())
);
}
class GameApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CultivatorComponent(),
);
}
}
class CultivatorComponent extends StatelessWidget{
#override
Widget build(BuildContext context) {
final cultivatorModel = Provider.of<CultivatorModel>(context);
final anotherModel = Provider.of<AnotherModel>(context);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('${cultivatorModel.cultivator} is here'),
Text('${anotherModel.modelNum} is here')
],
),
);
}
}
class CultivatorModel with ChangeNotifier{
String _cultivator;
String get cultivator => _cultivator;
CultivatorModel(){
_cultivator = "MyCultivator";
}
void addIncarnation(String incarnation){
_cultivator = incarnation;
notifyListeners();
}
}
class AnotherModel with ChangeNotifier{
int _modelNum;
int get modelNum => _modelNum;
AnotherModel(){
_modelNum = 55;
}
void changeNumber(int num){
_modelNum = num;
notifyListeners();
}
}
Related
I am creating a counter for a shopping cart. The problem is when the value of one counter changes, it affects all the other counters on the screen. I used ChangeNotifier and I have no idea what I am doing wrong.
This is the Counter Model
import 'package:flutter/material.dart';
class CounterModel extends ChangeNotifier {
int _count = 0;
int get getCount => _count;
void increment() {
if (_count >= 0) {
_count++;
}
notifyListeners();
}
void decrease() {
if (_count > 0) {
_count--;
}
notifyListeners();
}
}
This is the build of the Counter
import 'package:flutter/material.dart';
import 'package:provide_count/counter_model.dart';
import 'package:provider/provider.dart';
class ProvideCount extends StatelessWidget {
#override
Widget build(BuildContext context) {
final counter = Provider.of<CounterModel>(context);
return Row(
children: <Widget>[
IconButton(icon: Icon(Icons.add), onPressed: counter.increment),
Text('${counter.getCount}'),
IconButton(icon: Icon(Icons.remove), onPressed: counter.decrease),
],
);
}
}
And this is the main.dart file
import 'package:flutter/material.dart';
import 'package:provide_count/counter_model.dart';
import 'package:provide_count/providecount.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<CounterModel>(create: (_) => CounterModel()),
],
child: MaterialApp(
title: 'Flutter Demo',
home: SafeArea(
top: true,
child: Scaffold(
body: Column(
children: [
ProvideCount(),
ProvideCount(),
ProvideCount(),
],
),
),
),
),
);
}
}
The problem is that you have a ChangeNotifierProvider at the top of your tree.
When you call Provider.of<CounterModel>, You are searching for the closest Provider widget on the tree, Which is the same for all of the widgets. So they sync up.
If you don't want this to happen, you could stop using Provider, or you could at least wrap each ProvideCount with their own Provider:
return MaterialApp(
title: 'Flutter Demo',
home: SafeArea(
top: true,
child: Scaffold(
body: Column(
children: [
ChangeNotifierProvider<CounterModel>(
create: (_) => CounterModel()
child: ProvideCount(),
),
ChangeNotifierProvider<CounterModel>(
create: (_) => CounterModel()
child: ProvideCount(),
),
ChangeNotifierProvider<CounterModel>(
create: (_) => CounterModel()
child: ProvideCount(),
),
],
),
),
),
);
to make it simple , I'm creating a dashboard where I have a Drawer on the left side of the screen and Another widget that will change based on what the user will choose from Drawer.
Anyway , After adding the onPressed for a menu item , when I press to re-call a new widget next to the drawer I'm getting this error :
Could not find the correct provider<MenuController$> above MainScreen Widget.
So first this is my main.dart :
import 'package:admin/constants.dart';
import 'package:admin/controllers/MenuController.dart';
import 'package:admin/screens/dashboard/dashboard_screen.dart';
import 'package:admin/screens/main/main_screen.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Admin Panel',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: bgColor,
textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.white),
canvasColor: secondaryColor,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MenuController(),
),
],
child: MainScreen(DashboardScreen()),,
),
);
}
}
and this is my MenuController :
import 'package:flutter/material.dart';
class MenuController extends ChangeNotifier {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
GlobalKey<ScaffoldState> get scaffoldKey => _scaffoldKey;
void controlMenu() {
if (!_scaffoldKey.currentState.isDrawerOpen) {
_scaffoldKey.currentState.openDrawer();
}
}
}
and this is my main_screen :
import 'package:admin/controllers/MenuController.dart';
import 'package:admin/responsive.dart';
import 'package:admin/screens/dashboard/dashboard_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'components/side_menu.dart';
class MainScreen extends StatefulWidget {
Widget newWidget;
MainScreen(this.newWidget);
#override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
key: context.read<MenuController>().scaffoldKey,
drawer: SideMenu(),
body: SafeArea(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// We want this side menu only for large screen
if (Responsive.isDesktop(context))
Expanded(
// default flex = 1
// and it takes 1/6 part of the screen
child: SideMenu(),
),
Expanded(
// It takes 5/6 part of the screen
flex: 5,
child: DashboardScreen(),
),
],
),
),
);
}
}
and finally this is my side_menu :
DrawerListTile(
title: "Fournisseurs",
svgSrc: "assets/icons/menu_tran.svg",
subTitle1: 'Ajouter Un Fournisseur',
subTitle2: 'Liste Des Fournisseurs',
subTitle3: '---------',
press1: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MainScreen(Text('Hello'))),
);
},
After clicking on this in my Drawer I'm getting the error listed above.
As the error states, the correct provider isn't found above the MainScreen Widget.
See if it helps to structure your main.dart like this:
import 'package:admin/constants.dart';
import 'package:admin/controllers/MenuController.dart';
import 'package:admin/screens/dashboard/dashboard_screen.dart';
import 'package:admin/screens/main/main_screen.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MenuController(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Admin Panel',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: bgColor,
textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.white),
canvasColor: secondaryColor,
),
home: MainScreen(DashboardScreen()),
),
);
}
}
I'm trying to get my head around the Providers in Flutters... but after following some tutorials, I'm still facing some issue.
When I try to run this code, it gives me an error
Error: Could not find the correct Provider above this MyHomePage Widget
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_way/MyHomePageViewModel.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyHomePageViewModel(),
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Consumer<MyHomePageViewModel>(
builder: (context, viewModel, child) {
return Text(viewModel.text);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () =>
Provider.of<MyHomePageViewModel>(context, listen: false)
.onClicked(),
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
),
);
}
}
import 'package:flutter/foundation.dart';
class MyHomePageViewModel extends ChangeNotifier {
String text = 'Initial text';
void onClicked() {
text = 'Something was clicked';
notifyListeners();
}
}
The website where I found this example use it as
Provider.of<MainViewModel>(context, listen: false).onClicked(),
But that doesn't work either...
Before a widget that needs a provider is presented, it is required you create that particular provider before the page is built.
Checkout the working sample of your code below.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_way/MyHomePageViewModel.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => MyHomePageViewModel(),
builder: (_, __) => MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Consumer<MyHomePageViewModel>(
builder: (context, viewModel, child) {
return Text(viewModel.text);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () =>
Provider.of<MyHomePageViewModel>(context, listen: false)
.onClicked(),
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
import 'package:flutter/foundation.dart';
class MyHomePageViewModel extends ChangeNotifier {
String text = 'Initial text';
void onClicked() {
text = 'Something was clicked';
notifyListeners();
}
}
In your provider code, do you have a class defined like:
class MyHomePageViewModel extends ChangeNotifier {
// your stuff here, like getter and setters, methods, etc
}
That class will deal with all the centralisation of your states, essentially acting as you'd hope - the provider.
Can anyone please let me know why this code is not working? I have one data model file, One file for page where i have a search text and listview to display the names and main file. If i enter something on the search bar, names should be filtered. Please check the code and let me know what i have done wrong.
import 'package:flutter/widgets.dart';
import 'package:flutter/cupertino.dart';
import './class.dart';`enter code here`
class NameList with ChangeNotifier {
List<NameDetails> names = [
NameDetails('2', 'Mahesh'),
NameDetails('1', 'Ganesh'),
NameDetails('3', 'Suresh'),
NameDetails('4', 'Girish'),
];
List<NameDetails> get getNames {
return names;
}
void filterNames(String fil) {
List<NameDetails> names1;
names1 = names.where((n) => n.name.contains(fil));
names=names1;
notifyListeners();
}
}
import 'package:flutter/material.dart';
import 'package:practice/class.dart';
import 'package:provider/provider.dart';
import './data.dart';
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
NameList namesList;
String filterValue;
#override
Widget build(BuildContext context) {
final namesList = Provider.of<NameList>(context);
List<NameDetails> names = namesList.names;
return Scaffold(
appBar: AppBar(
title: TextField(
onChanged: (text) {
namesList.filterNames(text);
},
),
),
body: Column(
children: <Widget>[
Container(
height: 200,
child: Container(
height: 200,
child: ListView.builder(
itemBuilder: (ctx, index) {
return Card(
child: Text('${names[index].name}'),
);
},
itemCount: names.length,
),
),
),
],
));
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './mainScreen.dart';
import './data.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => NameList(),
child: MaterialApp(
title: 'Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainScreen(),
));
}
}
You can copy paste run full code below
Step 1: You can put filter function in getNames and use List<NameDetails> names = namesList.getNames;
Step 2: filterNames function only update _searchString and do notifyListeners()
code snippet
class NameList with ChangeNotifier {
List<NameDetails> names = ...
String _searchString = "";
UnmodifiableListView<NameDetails> get getNames => _searchString.isEmpty
? UnmodifiableListView(names)
: UnmodifiableListView(
names.where((n) => n.name.contains(_searchString)));
void filterNames(String fil) {
_searchString = fil;
print(_searchString);
notifyListeners();
}
}
...
Widget build(BuildContext context) {
final namesList = Provider.of<NameList>(context);
List<NameDetails> names = namesList.getNames;
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'dart:collection';
class NameDetails {
String id;
String name;
NameDetails(this.id, this.name);
}
class NameList with ChangeNotifier {
List<NameDetails> names = [
NameDetails('2', 'Mahesh'),
NameDetails('1', 'Ganesh'),
NameDetails('3', 'Suresh'),
NameDetails('4', 'Girish'),
];
String _searchString = "";
UnmodifiableListView<NameDetails> get getNames => _searchString.isEmpty
? UnmodifiableListView(names)
: UnmodifiableListView(
names.where((n) => n.name.contains(_searchString)));
void filterNames(String fil) {
_searchString = fil;
print(_searchString);
notifyListeners();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => NameList(),
child: MaterialApp(
title: 'Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainScreen(),
));
}
}
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
NameList namesList;
String filterValue;
#override
Widget build(BuildContext context) {
final namesList = Provider.of<NameList>(context);
List<NameDetails> names = namesList.getNames;
return Scaffold(
appBar: AppBar(
title: TextField(
onChanged: (text) {
namesList.filterNames(text);
},
),
),
body: Column(
children: <Widget>[
Container(
height: 200,
child: Container(
height: 200,
child: ListView.builder(
itemBuilder: (ctx, index) {
return Card(
child: Text('${names[index].name}'),
);
},
itemCount: names.length,
),
),
),
],
));
}
}
The Machinemodel instance from the Consumer is always null.
Why?
App
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Provider nested model demo',
theme: ThemeData(
// This is the theme of your application.
primarySwatch: Colors.blue,
),
home: ApplicationPage());
}
}
ApplicationPage
class ApplicationPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var newMachineEntity = new MachineEntity(1, "Power x1000"); //, engines);
return Scaffold(
appBar: AppBar(title: Text("Machine demo"), elevation: 6),
body: ChangeNotifierProvider(create: (context) => new MachineModel(newMachineEntity), child: MachineWidget()));
}
}
Widget
class MachineWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<MachineModel>(builder: (context, machine, child) {
////////////////////// machine instance is null /////////////
return Container(
child: Column(
children: <Widget>[
TextFormField(
initialValue: machine.name,
onChanged: (value) => machine.name = value,
),
],
),
);
});
}
}
Model
class MachineModel extends ChangeNotifier {
MachineEntity machineEntity;
// List<EngineModel> engines;
MachineModel(this.machineEntity) {
// engines = this.machineEntity.engines.map((eng) => new EngineModel(eng)).toList();
}
String get name => machineEntity.name;
set name(String value) {
machineEntity.name = value;
notifyListeners();
}
}
Entity
class MachineEntity {
int id;
String name;
// List<EngineEntity> engines;
MachineEntity(this.id, this.name); //, this.engines);
}