How to setState between different files - flutter

please somebody learn me how to do this
the below is very simple version of my code but i want it exactly like this
we have three different files. every one them has one statefull widget
like this
Home.dart
import 'package:flutter/material.dart';
import 'package:gtradar_test_1/GG/appbarz.dart';
import 'package:gtradar_test_1/GG/bodyz.dart';
class Home extends StatefulWidget {
const Home({ Key? key }) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Bodyz(),
appBar: PreferredSize(preferredSize: Size(20,20),child: AppBarz(),),
);
}
}
Bodyz.dart
import 'package:flutter/material.dart';
class Bodyz extends StatefulWidget {
const Bodyz({ Key? key }) : super(key: key);
#override
State<Bodyz> createState() => _BodyzState();
}
class _BodyzState extends State<Bodyz> {
bool xxx = false;
void xxx_Changer(){
setState(() {
xxx = !xxx;
});
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: xxx? Container(color: Colors.black):SizedBox(),
);
}
}
Appbarz
import 'package:flutter/material.dart';
class AppBarz extends StatefulWidget {
const AppBarz({ Key? key }) : super(key: key);
#override
State<AppBarz> createState() => _AppBarzState();
}
class _AppBarzState extends State<AppBarz> {
#override
Widget build(BuildContext context) {
return AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: ?,
),
)
;
}
}
ok,how can i call xxx_Changer() in appbar icon button on pressed or change xxx in appbar?

final ValueChanged onValueChanged;
above Field do the trick... you have to use this and can get the value from appbar class to homeclass then use setState to update your UI

You need to pass xxx_Changer as a function to the AppBarz and get xxx variable as a parameter in Bodyz widget. Like this;
Home.dart
import 'package:flutter/material.dart';
import 'package:gtradar_test_1/GG/appbarz.dart';
import 'package:gtradar_test_1/GG/bodyz.dart';
class Home extends StatefulWidget {
const Home({ Key? key }) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool xxx = false;
void xxx_Changer(){
setState(() {
xxx = !xxx;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Bodyz(xxx: xxx),
appBar: PreferredSize(preferredSize: Size(20,20),child: AppBarz(xxx_Changer: xxx_Changer),),
);
}
}
Bodyz.dart
import 'package:flutter/material.dart';
class Bodyz extends StatefulWidget {
final String xxx;
const Bodyz({ Key? key, this.xxx }) : super(key: key);
#override
State<Bodyz> createState() => _BodyzState();
}
class _BodyzState extends State<Bodyz> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: widget.xxx? Container(color: Colors.black):SizedBox(),
);
}
}
Appbarz
import 'package:flutter/material.dart';
class AppBarz extends StatefulWidget {
final Function xxx_Changer;
const AppBarz({ Key? key, Function xxx_Changer }) : super(key: key);
#override
State<AppBarz> createState() => _AppBarzState();
}
class _AppBarzState extends State<AppBarz> {
#override
Widget build(BuildContext context) {
return AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: widget.xxx_Changer(),
),
)
;
}
}

Related

Accessing a text field controller within another widget in Flutter?

Say I've created a file info_card.dart that I want to use to get the name of a user with the following code:-
import 'package:flutter/material.dart';
class InfoCard extends StatefulWidget {
const InfoCard({ Key? key }) : super(key: key);
#override
State<InfoCard> createState() => _InfoCardState();
}
class _InfoCardState extends State<InfoCard> {
final _nameController = TextEditingController();
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return TextField(
controller: _nameController
);
}
}
And then I create a completely different file, home_page.dart for example:-
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({ Key? key }) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: InfoCard(),
);
}
}
How would I access the text controller within the InfoCard() widget, and say store it in a variable so that I could write it to a database? I'm really struggling with this as simply trying to use InfoCard()._nameController doesn't seem to work.
You could create the controller in the home page instead and pass it to the infocard. for example:
class HomePage extends StatefulWidget {
const HomePage({ Key? key }) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _nameController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: InfoCard(controller: _nameController ),
);
}
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
}
and
class InfoCard extends StatefulWidget {
final TextEditingController controller;
const InfoCard({ Key? key, required this.controller}) : super(key: key);
#override
State<InfoCard> createState() => _InfoCardState();
}
class _InfoCardState extends State<InfoCard> {
#override
Widget build(BuildContext context) {
return TextField(
controller: widget.controller
);
}
}
There are many ways to achieve that in Flutter, the simplest thing you can do:
Instead of creating the controller inside InfoCard, inject it to it by its constructor. So your file will look like this:
class InfoCard extends StatefulWidget {
const InfoCard( this._nameController , { Key? key,}) : super(key: key);
final TextEditingController _nameController;
#override
State<InfoCard> createState() => _InfoCardState();
}
class _InfoCardState extends State<InfoCard> {
#override
Widget build(BuildContext context) {
return TextField(
controller: widget._nameController,
);
}
}
Create a controller in HomePage and pass it to InfoCard, thus, you'll have a reference to that controller in HomePage. So your file will look like this:
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _infoCardController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: InfoCard(_infoCardController),
);
}
}
Now pass that controller reference _infoCardController wherever you need it, and use simply as
print(_infoCardController.text)
Here i Solve this problem you can follow these steps:
just click the button and check the controller result in the console window
HomePage Code:
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> {
TextEditingController textController = TextEditingController();
void getText(){
print("entered text "+ textController.text);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InfoCard(textController),
ElevatedButton(onPressed: getText, child: Text("Press Here !")),
],
)),
);
}
}
InfoCard Page:
import 'package:flutter/material.dart';
class InfoCard extends StatefulWidget {
final TextEditingController textController ;
const InfoCard(#required this.textController);
#override
State<InfoCard> createState() => _InfoCardState();
}
class _InfoCardState extends State<InfoCard> {
// final _nameController = TextEditingController();
#override
void dispose() {
widget.textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.grey,
child: TextField(
controller: widget.textController
),
);
}
}

Prevent children from rebuilding in SliverList

Minimum example:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
body: CustomScrollView(
slivers: [Example()],
),
));
}
}
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<Example> createState() => ExampleState();
}
class ExampleState extends State<Example> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return SliverList(
delegate: SliverChildListDelegate(const <Widget>[
SizedBox(
height: 1400,
),
CheckboxWidget()
], addAutomaticKeepAlives: true));
}
#override
bool get wantKeepAlive => true;
}
class CheckboxWidget extends StatefulWidget {
const CheckboxWidget({Key? key}) : super(key: key);
#override
State<CheckboxWidget> createState() => _CheckboxWidgetState();
}
class _CheckboxWidgetState extends State<CheckboxWidget> {
late bool _personalData = false;
#override
Widget build(BuildContext context) {
return Checkbox(
checkColor: Colors.black,
onChanged: (bool? value) {
setState(() {
_personalData = value!;
});
},
value: _personalData,
);
}
}
If you click the checkbox, then scroll out of view and then back in to view.. The box becomes unchecked. This is because the widget rebuilds...setting the _personalData to false. I would of thought addAutomaticKeepAlives would prevent the widget rebuilding and keep the state of the checkbox. How do I prevent CheckboxWidget from rebuilding?
Firstly, I will choose state management or passing value to the CheckboxWidget. To answer this question, we need to save (keep alive) the state of CheckboxWidget. Therefore, we need to use AutomaticKeepAliveClientMixin on _CheckboxWidgetState instead of parent widget.
class CheckboxWidget extends StatefulWidget {
const CheckboxWidget({Key? key}) : super(key: key);
#override
State<CheckboxWidget> createState() => _CheckboxWidgetState();
}
class _CheckboxWidgetState extends State<CheckboxWidget>
with AutomaticKeepAliveClientMixin {
late bool _personalData = false;
#override
Widget build(BuildContext context) {
super.build(context);
return Checkbox(
checkColor: Colors.black,
onChanged: (bool? value) {
setState(() {
_personalData = value!;
});
},
value: _personalData,
);
}
#override
bool get wantKeepAlive => true;
}
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<Example> createState() => ExampleState();
}
class ExampleState extends State<Example> {
#override
Widget build(BuildContext context) {
return SliverList(
delegate: SliverChildListDelegate(
const <Widget>[
SizedBox(
height: 1400,
),
CheckboxWidget()
],
),
);
}
}

Flutter - Update child state from parent

I would like to update a child's state when a button is clicked in the parent, so for example:
class Parent extends StatelessWidget{
Widget build(context){
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
onPressed: () => //somehow increment the child's counter,
icon: const Icon(Icons.add),
),
],
),
body: const Child(),
);
}
}
class Child extends StatefulWidget {
const Child({Key? key}) : super(key: key);
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
...
int counter = 0; //to be incremented when parent's button is clicked on.
...
}
Is there a common way to implement this? From the other posts I've read, people usually use the child to update the parent's state via callback, so if there is a way to refactor my code to acheive the same effect, that would help too.
You can create the field counter in the parent and pass it down to the child widget and update the child widget from the parent.
You can check the demo that I made here..
DartPad Demo Link
statemanagement Method
You can use provider,bloc,cubit,getx... package to update the child and parent value
setstate callback (here i mention)
Change you widget like this .your parent widget to stateful.
int counter = 0;
class Parent extends StatefulWidget {
#override
State<Parent> createState() => _ParentState();
}
class _ParentState extends State<Parent> {
Widget build(context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
onPressed: () {
setState(() {
counter++;
});
},
icon: const Icon(Icons.add),
),
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
Child({Key? key}) : super(key: key);
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return Center(child: Text("$counter",style: TextStyle(fontSize: 30),));
} //to be incremented when parent's button is clicked on.
SampleCod Dartpad live code check here
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Parent(),
);
}
}
class Parent extends StatefulWidget {
#override
State<Parent> createState() => _ParentState();
}
class _ParentState extends State<Parent> {
Widget build(context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
onPressed: () {
setState(() {
counter++;
});
},
icon: const Icon(Icons.add),
),
],
),
body: Child(),
);
}
}
int counter = 0;
class Child extends StatefulWidget {
Child({Key? key}) : super(key: key);
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return Center(child: Text("$counter",style: TextStyle(fontSize: 30),));
} //to be incremented when parent's button is clicked on.
}
Try this:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Parent(),
);
}
}
class Parent extends StatefulWidget {
const Parent({Key? key}) : super(key: key);
#override
State<Parent> createState() => _ParentState();
}
class _ParentState extends State<Parent> {
int counter = 0;
void incrementCounter() {
setState(() {
counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
tooltip: "Increment counter",
onPressed: incrementCounter,
icon: const Icon(
Icons.add,
),
),
],
),
body: Child(
counter: counter,
),
);
}
}
class Child extends StatefulWidget {
const Child({
Key? key,
required this.counter,
}) : super(key: key);
final int counter;
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return Center(
child: Text(
widget.counter.toString(),
style: const TextStyle(
fontSize: 30,
),
),
);
}
}

Call Navigator from outside state or call child state method from parent

I ended up with a parent calling a child method, which is fine, but I can't call Navigator outside the state class. My goal is either to move the child method in it's state and somehow access it, or to call Navigator form outside the state in a Stateful widget.
What is the best approach to this problem?
import 'package:flutter/material.dart';
class ParentClass extends StatefulWidget {
const ParentClass({Key? key}) : super(key: key);
#override
_ParentClassState createState() => _ParentClassState();
}
class _ParentClassState extends State<ParentClass> {
ChildClass childclass = ChildClass();
#override
Widget build(BuildContext context) {
return Container(
child: ElevatedButton(
child: Text('Button'),
onPressed: () => {
childclass.callNavigator(),//or call callNavigatorState
}),
);
}
}
class ChildClass extends StatefulWidget {
const ChildClass({Key? key}) : super(key: key);
void callNavigator() {
//call navigator from here
}
#override
_ChildClassState createState() => _ChildClassState();
}
class _ChildClassState extends State<ChildClass> {
void callNavigatorState(){
//access from parent widget?
}
#override
Widget build(BuildContext context) {
return Container();
}
}
you can access a stateful's state, using Globalkey.currentState...
check this sample code:
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key}) : super(key: key);
final child = ChildWidget(key: GlobalKey());
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
child,
ElevatedButton(
onPressed: () {
(((child as ChildWidget).key as GlobalKey).currentState!
as _ChildWidgetState)
.someFunction();
},
child: Text("childWidget someFunction"))
],
),
);
}
}
class ChildWidget extends StatefulWidget {
const ChildWidget({Key? key}) : super(key: key);
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
void someFunction() {
print("childWidget someFunction");
}
#override
Widget build(BuildContext context) {
return Container();
}
}

Event Listeners in flutter

Is there a way I can listen to a button click in flutter? I want to know if the button is clicked in another class that creates a object of that button.
What i want to do is let MyButtonRow know when MyButton is clicked, since it involves changing variables in MyButtonRow.
The functionality i am looking for is similar to .setEventListener in JavaScript
So here's the code
class MyButtonRow extends StatefulWidget {
const MyButtonRow({Key? key}) : super(key: key);
#override
_MyButtonRowState createState() => _MyButtonRowState();
}
class _MyButtonRowState extends State<MyButtonRow> {
#override
Widget build(BuildContext context) {
return Container(width: MediaQuery.of(context).size.width,
child: Row(children: <Widget>[MyButtonTile()],));
}
}
class MyButtonTile extends StatefulWidget {
const MyButtonTile({Key? key}) : super(key: key);
#override
_MyButtonTileState createState() => _MyButtonTileState();
}
class _MyButtonTileState extends State<MyButtonTile> {
#override
Widget build(BuildContext context) {
return TextButton(onPressed: (){
//notify MyButtonRow about the click
}, child: Text("hello"));
}
}
Firstly you declare onTap function on your child widget and then just pass the onTap function from where you define the MyButtonTile
class MyButtonRow extends StatefulWidget {
const MyButtonRow({Key? key}) : super(key: key);
#override
_MyButtonRowState createState() => _MyButtonRowState();
}
class _MyButtonRowState extends State<MyButtonRow> {
#override
Widget build(BuildContext context) {
return Container(width: MediaQuery.of(context).size.width,
child: Row(children: <Widget>[MyButtonTile(onTap: (){
print("Notify me");
})],));
}
}
class MyButtonTile extends StatelessWidget {
final Function onTap;
MyButtonTile({this.onTap});
#override
Widget build(BuildContext context) {
return TextButton(onPressed:onTap,//notify MyButtonRow about the click
child: Text("hello"));
}
}