Consumer of ChangeNotifierProvider is always null - flutter

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);
}

Related

How do I pass the data from the checked List Tile to the next page (Basket) in flutter

I previously used a Listview to present my data, but I was advised to use a Checkbox List tile now I'm stuck on how to pass data. I want to pass the selected/checked List tile's data to the next page (Basket). How do I do that, please help me.
I am new to Flutter.
The Main Method Class
import 'package:cart_app1/Basket.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(),
Basket.routeName: (context) => Basket(),
},
);
}
}
The product list class
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Map<String, bool> values = {
"GIN COCKTAILS \nClover Club \t R65": false,
"Beers & Ciders \nHeineken NRB \t R29": false,
"Vodka \nCiroc \t R35": false,
"Vodka \nCruz Vodka \t R30": false,
"COGNAC \nHennesy \t R40": false,
"Tequilla \nEl Jimador \t R30": false,
"Non-Alcoholic \nSoft-Drink \t R20": false,
"WHISK[E]Y \nJohnie Walker Red \t R25": false,
};
var toggle = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("The Wing Republic"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.shopping_basket),
onPressed: () {
Navigator.of(context)
.pushNamed(Basket.routeName, arguments: values);
},
),
],
),
body: ListView(
children: values.keys.map((String key) {
return CheckboxListTile(
title: Text(key),
subtitle: Text(""),
isThreeLine: false,
value: values[key],
onChanged: (bool value) {
setState(() {
values[key] = value;
});
},
);
}).toList(),
),
);
}
}
The Basket Method
import 'package:flutter/material.dart';
class Basket extends StatelessWidget {
static const routeName = '/Basket';
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context).settings.arguments as Map<String, bool>;
return Scaffold();
}
}
You can pass arguments in pushNamed method.
Navigator.of(context).pushNamed(Basket.routeName, arguments: values);
On the receiving end, use ModalRoute to get the values.
import 'package:flutter/material.dart';
class Basket extends StatelessWidget {
static const routeName = '/Basket';
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context).settings.arguments as Map<String, bool>;
return Scaffold();
}
}
Routes in MyApp is incorrect. If you want to use routes, you can't use home property. Instead of home specify initialRoute.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(),
Basket.routeName: (context) => Basket(),
},
);
}
}

Flutter Provider Issue - Changes are not reflecting

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,
),
),
),
],
));
}
}

No able to get the latest state of an object using provider

I am trying get the latest state of the name variable from the Home class, but I am getting a null instead of the value Tenzin Nyidon. I initialize the name variable inside the build method of the MyHomePage as you can see, and set the notifyListeners() method (which notify all listeners) inside the onPress of Navigate button.
main.dart:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<MyHomePage>(builder: (_) => MyHomePage(),)
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatelessWidget with ChangeNotifier {
String name;
#override
Widget build(BuildContext context) {
//* Here I intialized the name variabel
name = "Tenzin Nyidon";
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(name),
RaisedButton(
child: Text("Navigate"),
onPressed: (){
//* notify all listener
notifyListeners();
Navigator.of(context).push(
MaterialPageRoute(
builder: (_){
return Home();
}
)
);
},
)
],
),
),
);
}
}
home.dart:
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
var provider = Provider.of<MyHomePage>(context);
return Scaffold(
body: Center(
//! I am getting a null value here
child: Text("name from the main.dart: ${provider.name}"),
),
);
}
}
try to not create a provider with a stateless widget because it can't update its state.
and also you have to create a method to handle changes and then call changeNotifier();
such as:
class MyProvider extends ChangeNotifier {
String name;
void changeName(String newValue){
name = newValue;
}
}
and then call in provider:
To change value:
Provider.of<MyProvider>(context).changeName(newValue);
To read it:
Provider.of<MyProvider>(context).name;

how to edit value at the child widget

i try to edit the value of the child widget, i can do it with StatefulWidget parent but i want to do it with StatelessWidget parent and without using global value
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(child: Text('addFile'), onPressed: () {}),
FlatButton(child: Text('deleteFile'), onPressed: () {})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
Widget build(BuildContext context) {
return hasFile ? Text('has a file') : Text("no File");
}
}
You are thinking the wrong way. Child aka Text() should get its value from a model which is managed by the application or at least managed by the widget above. I would go with the provider package https://pub.dev/packages/provider and do this:
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
class MyState with ChangeNotifier {
String _myText;
MyState(this._myText);
getMyText() => _myText;
void changeText(String newText) {
_myText = newText;
notifyListeners();
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => MyState("initial Text")),
],
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("addFile");
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("deleteFile");
})
],
),
body: Child(),
));
}
}
class Child extends StatelessWidget {
#override
Widget build(BuildContext context) {
MyState myState = Provider.of<MyState>(context);
return Text(myState.getMyText());
}
}
This is coded without IDE support or even compiling and running. But it should get you to the right direction.
You can use BLoC pattern to implement this kind of functionality,
Here is the BLoC class which will handle state of bool
import 'dart:async';
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();
Then you can add stream builder in your Stateful Widget, in which you will provide stream of BLoC class.
StreamBuilder updates it's UI according to Stream.
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
At last you can access BLoC class with your stateless widget as follows
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
Full example is as below
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Homepage(),
);
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();

How can I rerend data from Stream in Stateless Widget?

I am trying to re-rend Stateless widget every time when I am getting new data from Stream. But it does not work, and I can't understand when I should to call re-rend function:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Stream<int> stream; // I maked it's global for simplification
void main()
{
MyClass myClass = MyClass();
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft])
.then((_) {
runApp(new MyApp());
});
}
class MyClass
{
MyClass()
{
stream = Stream<int>.periodic(Duration(seconds: 1), (t) => t+1).take(9);
print("hello");
}
}
class MyApp extends StatelessWidget
{
#override
Widget build(BuildContext context)
{
return MaterialApp(
title: "Hello",
routes: {
'/' : (context) => SplashScreen(),
}
);
}
}
class SplashScreen extends StatelessWidget {
#override
Widget build(BuildContext context)
{
return Scaffold(
appBar: AppBar(),
body: Container(
child: Text("Hello World, $stream"),
),
);
}
}
please check example below
Both Parent and Child Widget are Stateless.
Use simple class to wrap stream controller, here class name is bloc
Child widget use StreamBuider
In parent widget call bloc function and pass parameter and then child widget will render
code snippet
bloc.changeState(true)
...
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
full code
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Homepage(),
);
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
class Child extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();