could you please show me how can I notify my statefull child widget that somewhere in parent user clicks on button?
I have two separate .dart files
in the first file I described main screen widget with FAB
and in the second one I have ListWidget (like RecyclerView)
If user tap on FAB I want notify my ListWidget about it so it can e.g. add one more item.
I have java/android background but it's quite hard for me to change my mind flow.
The first option would be to build the child widget each time you add an item to the list, passing the list as a parameter to the child.
But using streams is a nice way to avoid rebuilding the child widget each time. I think the following code is a good starting point (You could also use a StreamBuilder to build the list leveraging the stream).
In main.dart
import 'dart:async';
import 'package:base_test_project/expanding_list.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
StreamController<int> _controller = StreamController<int>();
int _number = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new ExpandingList(stream: _controller.stream),
),
floatingActionButton: new FloatingActionButton(
onPressed: () {_controller.add(_number++);},
child: new Icon(Icons.add),
),
);
}
}
In expanding_list.dart
import 'dart:async';
import 'package:flutter/material.dart';
class ExpandingList extends StatefulWidget {
Stream<int> stream;
ExpandingList({this.stream});
#override
_ExpandingListState createState() => _ExpandingListState();
}
class _ExpandingListState extends State<ExpandingList> {
List<int> _myList = [];
#override
void initState() {
super.initState();
widget.stream.listen((number) {
setState(() { _myList.add(number); });
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _myList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.all(15.0), child: Text("Item ${_myList[index]}"));
});
}
}
Related
I am trying to use hive to store data on a local machine using hive but each time when I compile the code it gives the error "The box "notebook" is already open and of type Box."
Can someone help me to resolve the issue as I am new to it? Thanks
I am just trying to add data to the database in this app without any change to the state of the app interface. I have tried to change the main method to void but no luck on this.
All the code is located in the main file
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'notes.dart';
import 'notesStoring.dart';
Future main() async{
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
Hive.registerAdapter(NotesAdapter());
await Hive.openBox<NotesAdapter>('noteBook');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void dispose() {
Hive.close();
// TODO: implement dispose
super.dispose();
}
#override
Future incrementCounter(String title) async {
final notes = Notes()
..title = title;
final box =Boxes.getNotesValues();
box.add(notes);
}
final titleForNotes=TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body:
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
TextField(
controller: titleForNotes,
cursorColor: Colors.pink,
),
ValueListenableBuilder<Box<Notes>>(valueListenable: Boxes.getNotesValues().listenable(), builder: (context,box,_){
final noteBook =box.values.toList().cast<Notes>();
return buildContent(noteBook);
})
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
incrementCounter(titleForNotes.text);
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class Boxes {
static Box<Notes> getNotesValues()=>Hive.box<Notes>('noteBook');
}
Widget buildContent(List<Notes> noteBook){
return Column(
children: [
Expanded(child:
ListView.builder(
padding: EdgeInsets.all(8),
itemCount: noteBook.length,
itemBuilder: (BuildContext context, int index){
final notes= noteBook[index];
return buildTransaction(context, notes);
}
)
)
],
);
}
Widget buildTransaction(
BuildContext context,
Notes notes,
){
return Card(
color: Colors.green,
child: Text(notes.title),
);
}
1.You can open your notebook Box in the main method of your app:
Future<void> main() async {
...
final appDocumentDirectory = await
path_provider.getApplicationDocumentsDirectory();
Hive.init(appDocumentDirectory.path);
Hive.registerAdapter(UserAdapter());
// open the user box
await Hive.openBox('notebook');
_setUpLogging();
runApp(MultiProvider(providers: providers, child:
StartupApplication()));
}
2 Access the previously opened box like below:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// user box
Box notebookBox;
#override
void initState() {
super.initState();
// get the previously opened user box
notebookBox = Hive.box('notebook');
}
#override
Widget build(BuildContext context) {
// check for your conditions
return (notebookBox.values.isNotEmpty && notebookBox.get(0).active == 1)
? HomeView()
: Intro();
}
}
Question:
How to call methodA() from onPressed() of IconButton.
I've tryed to do this by using GlobalKey:
GlobalKey<_MyButtonState> globalKey = GlobalKey();
But it's returns an error.
I have read many forums on this and I have tried all the solutions posed but none of them are working for me.
CODE:
main.dart
import 'package:flutter/material.dart';
import 'button.dart';
void main() {
runApp(MaterialApp( title: 'My app', home: MyApp(),));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.help),
onPressed: () {
// how can I call methodA from here?
},
),
),
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: MyButton(),
);
}
}
button.dart
import 'package:flutter/material.dart';
class MyButton extends StatefulWidget {
#override
_MyButtonState createState() => _MyButtonState();
}
class _MyButtonState extends State<MyButton> {
#override
Widget build(BuildContext context) {
return Container( );
}
void methodA(){
print('methodA');
}
}
I have read many forums on this and I have tried all the solutions posed but none of them are working for me.
first, you will have to import the file as a package in main.dart:
Main.dart: (just writing the way to import the file)
import 'package:prioject_name/file_name.dart';
Note: this is for files under lib directory.
if your file is under a different directory inside lib
then add the path accordingly,
eg: Button.dart is inside the widgets folder inside the lib folder:
lib
|____widgets
|____Button.dart
then the import statement will be as follows:
import 'package:prioject_name/widgets/Button.dart';
Then try your global key method to call the function:
If it is still not working then you can use my method,
how I call methods from different class in onPressed or onTapped:
your Button.dart file.
import 'package:flutter/material.dart';
// changed the method definition class
class MyButton extends StatefulWidget {
void methodA(){
print('methodA');
}
#override
_MyButtonState createState() => _MyButtonState();
}
class _MyButtonState extends State<MyButton> {
#override
Widget build(BuildContext context) {
...
widget.methodA(); // this would call the method A, anywhere inside the Widget build() function.
return Container( );
}
}
Now in Main.dart:
import 'package:prioject_name/Button.dart';
//call the function here using className().functioName();
....
onPressed(){
MyButton().methodA();
}
Take a look at the InheritedWidget class (and watch the videos).
Base class for widgets that efficiently propagate information down the
tree.
You can look at creating an InheritedWidget that contains a ValueNotifier.
class MyInheritedWidget extends InheritedWidget {
final ValueNotifier<int> buttonTapCountNotifier;
const MyInheritedWidget({
Key key,
#required this.buttonTapCountNotifier,
#required Widget child,
}) : assert(child != null),
super(key: key, child: child);
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
}
Your MyButton class can call MyInheritedWidget.of(context).buttonTapCountNotifier to get hold of the ValueNotifier and add a listener to it.
Each time the ValueNotifier notifies your MyButton class that the value has been incremented, you can execute methodA.
You could use the Provider package which is quite the preferred method to manage state in Flutter apps. This will help you as well in organizing and growing the app in a clever way.
Take a look at the working code below.
define a ChangeNotifier (PressedProvider) which will save
current state of the app in a unique location and the behavior of your onPress function
you wrap your app
with a ChangeNotifierProvider widget
you wrap the receiving
Widget with a Consumer
you get the Provider.of() when you
need to do something and call a method on it
it will notify the Consumer of a change
Code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(ChangeNotifierProvider<PressedProvider>( // 2
create: (_) => PressedProvider(),
child: MyApp(),
));
}
class PressedProvider extends ChangeNotifier { // 1
void pressButton() {
print("pressButton");
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
leading: Consumer<PressedProvider>( // 3
builder: (_, provider, widget) => IconButton(
icon: Icon(Icons.help),
onPressed: () {
provider.pressButton();
},
),
),
),
body: Center(
child: MyButton(),
),
),
);
}
}
class MyButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
PressedProvider provider = Provider.of<PressedProvider>(context); // 4
return Center(
child: RawMaterialButton(
child: Text("Press me"),
onPressed: () => provider.pressButton()),
);
}
}
I want to call the method from HomeState class to _MyHomePageState class.But i have no idea to do that.
this is main.dart :
import 'package:flutter/material.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) {
Home home = new Home();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){}
),
);
}
}
Home class:
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return HomeState();
}
}
class HomeState extends State<Home> {
int numberPrint(){
setState((){});
}
#override
Widget build(BuildContext context) {
}
}
I want to call the method numberPrint() in floatingbutton in _MyHomePageState class in main.dart.
Please help me to do that.
Please take a look at how I would have handled, this is not the best, but it would give you a better idea.
import 'package:flutter/material.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'),
);
}
}
//MyHomePage---------------------------------------------------
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
MyController controller = MyController();
#override
Widget build(BuildContext context) {
Home home = new Home(controller:controller);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.execute,
),
);
}
}
//Home---------------------------------------------------------------------
class Home extends StatefulWidget {
Home({this.controller});
final MyController controller;
#override
State<StatefulWidget> createState() {
return HomeState();
}
}
class HomeState extends State<Home> {
initState(){
widget.controller.addListener(numberPrint);
}
numberPrint(){
setState((){});
}
#override
Widget build(BuildContext context) {
}
}
//MyController--------------------------------------------------------
class MyController{
Function listener;
addListener(Function fn){
listener = fn;
}
execute(){
listener();
}
}
In general, I use some kind of page nerve center through which widgets can register their setState functions or other things such as FocusNode and even data.
For example,
class PageNerveCenter{
Function requireHomeRebuild;
}
PageNerveCenter _pageNerveCenter = PageNerveCenter();
class Home extends StatefulWidget{...}
class _HomeState extends State<Home>{
...
#override
initState(){
super.initState();
// register setState
_pageNerveCenter.requireHomeRebuild = (){
setState((){})
};
}
#override
dispose(){
// remove setState when object is disposed
_pageNerveCenter.requireHomeRebuild = (){};
}
...
}
In some other widget (same file/library),
_pageNerveCenter.requireHomeRebuild();
This approach then allows you to call a widget's setState from anywhere in the file, from inside another widget.
In my own experience, this approach has helped to easily break down my widget tree and only rebuild those widgets that actually need to rebuilt too.
I would certainly like to hear other more experienced flutter developers about their take on this approach since I am relatively new too.
I am working on a flutter app, and I have some data stored in the state of a widget. In this case it is the string title. I am wondering if I can pass this data through a parent stateless widget and into this stateless widgets parent, which is a stateful widget. If working correctly, I could pass title into the state of MyHomePage and save it into title2. Is there a way to do this or do I have to convert Widget1 into a stateful widget. The only issue with that is that I already wrote the widget, but I am curious. Here is my code. Thanks!
//main.dart
import 'package:flutter/material.dart';
import 'Widget1.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(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title2;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Center(
child: Widget1(),
),
);
}
}
/////////////////////////////////////////////
//Widget1.dart
Widget Widget1() {
return Widget2();
}
/////////////////////////////////
//Widget2.dart
import 'package:flutter/material.dart';
class Widget2 extends StatefulWidget {
final String title = "Hello from Widget2";
_Widget2State createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
String title = "Hello from Widget2";
#override
Widget build(BuildContext context) {
return Text('${title}');
}
}
Thanks again!
If you are not using any state management except default one then you can pass data between widgets using Navigator. Here is the code example of how to pass String from child Stateless widget (can be stateful too) to its parent widget.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FIRST WIDGET"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Title from child Stateless widget: $title"),
FlatButton(
onPressed: () => _openSecondWidget(),
child: Text("OPEN SECOND WIDGET"),
)
],
),
),
);
}
void _openSecondWidget() async {
var newTitle = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondWidget(),
),
);
setState(() {
title = newTitle;
});
}
}
class SecondWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SECOND WIDGET"),
),
body: Center(
child: FlatButton(
onPressed: () {
Navigator.of(context).pop("Hi from Second Widget");
},
child: Text("GO BACK"),
),
),
);
}
}
So the button on the first widget is pushing new widget on the screen and awaits for its result. When it gets the result from the second widget I'm using setState updating display of the title variable. And second widget has just one button which removes this widget from the back stack with some parameter which is in this case String, but it can be anything else.
I assume you just want to pass data from StatelessWidget to StatefulWidget and want to access it in its State. Then try this,
import 'package:flutter/material.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",),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title), //TODO: use `widget` to access properties
),
body: Center(
child: MyWidget(title: widget.title,),
),
);
}
}
class MyWidget extends StatefulWidget {
final String title;
const MyWidget({Key key, this.title}) : super(key: key);
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
#override
Widget build(BuildContext context) {
return Text('${widget.title}');
}
}
I am working on a flutter app and I want to update the second level parent's state when a button is pressed. When the "PressMe" button is pressed, I want MyHomePage's state to have Widget2's title string saved in it's own state. Can someone please help me out with this? The button is a lower level widget and I want to pass the data up two levels. Thanks!
//main.dart
import 'package:flutter/material.dart';
import 'Widget1.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(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title2;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Center(
child: Widget1(),
),
);
}
}
///////////////////////////////
//Widget1.dart
import 'package:flutter/material.dart';
import 'Widget2.dart';
class Widget1 extends StatefulWidget {
_Widget1State createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
return Widget2();
}
}
///////////////////////////////
//Widget2.dart
import 'package:flutter/material.dart';
class Widget2 extends StatefulWidget {
final String title = "Hello from Widget2";
_Widget2State createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
String title = "Hello from Widget2";
#override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: null,
child: Text(
'PressMe',
style: TextStyle(fontSize: 20)
),
);
}
}
Thanks!
The easiest way to update your parent widget/class from a child is to pass down a function you create in the parent, then call that function from your child when you need to update it. However, that gets messy if you need to pass it down through multiple children. Usually in this case you'll want your parent to be a StatefulWidget and call setState inside the function you create when you assign the new title.
The next solution is to use InheritedWidgets or ChangeNotifiers.
The ideal solution would be to use some form of state management such as Provider or Bloc.