So I am trying to change a page in Flutter, here's my code:
class _RootAppState extends State<RootApp> {
int tabIndex = 0;
final pages = [
Start(),
Quiz(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueAccent,
body: getBody(),
);
}
Widget getBody() {
return pages[tabIndex];
}
}
and I thought I will create a constructor for Start() and Quiz() class to pass tabIndex argument because the other pages need this integer to be used for another change.
But passing an argument in an array gets me an error
The instance member 'tabIndex can’t be accessed in an initializer.
I can pass an argument in
body: Start(tabIndex)
But what if I wanted to do it in an array of my pages? Is this possible or should I look for another solution?
Thank you
You must ensure that Start() and Quiz() are Widget
But maybe you should take a closer look at PageView.
If you refactor the initialization of tabIndex and pages to be handled by the constructor, then you should be able to initialize pages as described:
class RootApp extends StatefulWidget {
const RootApp({Key? key}) : super(key: key);
#override
_RootAppState createState() => _RootAppState();
}
class _RootAppState extends State<RootApp> {
_RootAppState({this.tabIndex = 0})
: pages = [
Start(tabIndex: tabIndex),
Quiz(tabIndex: tabIndex),
];
int tabIndex;
final List<Widget> pages;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueAccent,
body: getBody(),
);
}
Widget getBody() {
return pages[tabIndex];
}
}
class Start extends StatelessWidget {
const Start({Key? key, required this.tabIndex}) : super(key: key);
final int tabIndex;
#override
Widget build(BuildContext context) => Text('Start Placeholder $tabIndex');
}
class Quiz extends StatelessWidget {
const Quiz({Key? key, required this.tabIndex}) : super(key: key);
final int tabIndex;
#override
Widget build(BuildContext context) => Text('Quiz Placeholder $tabIndex');
}
Related
home.dart
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late Directory? appDir;
late List<String>? records;
record.dart
class Records extends StatefulWidget {
final List<String> records;
const Records({
Key? key,
required this.records,
}) : super(key: key);
#override
_RecordsState createState() => _RecordsState();
}
app.dart
class App extends GetView<AppController>{
const App({Key? key}) : super(key: key);
static const PrimaryColor1 = const Color(0xFF708FF8);
#override
Widget build(BuildContext context){
return Scaffold(
body: Obx(() {
switch(RouteName.values[controller.currentIndex.value]) {
case RouteName.Home:
return Home();
break;
case RouteName.Record:
return Record(records: records);
I need to return Record(records: records) in app.dart. The variable records is in home.dart. Is there a way to use the variable records in app.dart?
+) I tried changing it as follows, but 'recordnull' was printed.
home.dart
class video_Body extends StatefulWidget {
video_Body({Key? key, required this.index}) : super(key: key);
#override
_video_Body createState() => _video_Body();
int index;
static List<String>? records;
}
class _video_Body extends State<video_Body> {
List<String>? get record => video_Body.records;
app.dart
if(video_Body.records == null) print('recordnull');
return Record(records: video_Body.records);
There are several ways to do what you want to do, here are the two simplest ones I could think of:
1. Store the variable somewhere else.
It is hard to explain how to do this because I have very little information about what the home page is doing to the list of records, but the general idea is declare the list of records on the app.dart file and pass the value of the list into the Home class.
To do this, you will probably have to declare some sort of callback for when the Home class wants to edit the list, I will give you an example:
In this example, we have a button widget that displays a number, and when you press the button it increases the value of the number, and we want to access the current value of the number.
First I will show you the equivalent of your problem:
Button:
class MyButton extends StatefulWidget {
...
}
class _MyButtonState extends State<MyButton> {
int value = 0;
#override
Widget build(BuildContext) {
return ElevatedButton(
child: Text(value.toString()),
onPressed: () => setState(() => value++),
);
}
}
Home:
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [
MyButton(),
Text(buttonText), // how do we access the button text?????
]
);
}
}
And now, with the solution:
Button:
class MyButton extends StatelessWidget {
MyButton({required this.value, required this.onChange});
final void Function(int) onChange;
final int value;
#override
Widget build(BuildContext) {
return ElevatedButton(
child: Text(value.toString()),
onPressed: () => onChange(value +1),
);
}
}
Home:
class Home extends StatefulWidget {
...
}
class _HomeState extends State<Home> {
int value = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
MyButton(onChange: (v) => setState(() => value=v), value: value),
Text(value.toString()),
]
);
}
}
Of course, this solution may not work for you depending on the situation.
2. Use a static class
The idea here is to move the variable you want to access into a static class, first you need to make a class with a static variable you want to access:
class RecordsService {
static List<String>? records;
}
Because it is static, you can access it from anywhere, so on home, make the following changes:
Before:
late List<String>? records;
After:
List<String>? get records => RecordService.records;
and to access the value on app:
return Record(records: RecordService.records);
Hopefully one of these solutions solves your use case.
I want to change the value of the text field in MyButton, such that, each time the button is clicked, the value is changed. I have set the value as a attribute in MyButton and passed a function from MyButtonRow to MyButton that will be executed everytime it is clicked. In that function, I want to change the attribute "myString" of MyButton, so it will automatically update the text field.
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>[MyButton(onTap: (){
print("Notify me");
//I want to change the myText attribute of MyButton here
}, myText: "Hello",)],));
}
}
class MyButton extends StatefulWidget {
final Function onTap;
final String myText;
const MyButton({Key? key, required this.onTap, required this.myText}) : super(key: key);
#override
_MyButtonState createState() => _MyButtonState();
}
class _MyButtonState extends State<MyButton> {
var onTap;
var myString;
#override
void initState() {
onTap = widget.onTap;
myString = widget.myText;
super.initState();
}
#override
Widget build(BuildContext context) {
return TextButton(onPressed:onTap,
child: Text(myString));
}
}
**EDIT**
Both the answers, by #Yeasin Sheikh and #Jav T were correct.
But this brought me to another question, "Is there a way i can access that attribute of MyButton in MyButtonRow"
Issue is that you are passing the newValue as a constructor parameter and assigning it to a local variable which is retained in state. Whenever a rebuild is called, the initState function is never called again, just the first time, I would recommend that instead of using:
#override
Widget build(BuildContext context) {
return TextButton(onPressed:onTap,
child: Text(myString));
}
You go ahead and use:
#override
Widget build(BuildContext context) {
return TextButton(onPressed:onTap,
child: Text(widget.myText));
}
In this case, the value will be updated whenever the parent rebuilds your widget. Make sense?
However, you cannot change the attributes of a widget after it is built - How to access/change an attribute of a Widget in Flutter
You can create a method or assign method like inside _MyButtonState's build
onPressed: () {
onTap();
setState(() {
myString = "newValue";
});
},
To use MyButton's myText that is coming though constructor, use widget.myText as #Jav T descried.
It is not necessary to make this StatefulWidget in this case, you can simply use StatelessWidget for MyButton.
class MyButton extends StatelessWidget {
final Function onTap;
final String myText;
const MyButton({
Key? key,
required this.onTap,
required this.myText,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextButton(
onPressed: () => onTap(),
child: Text(myText),
);
}
}
I want to push param value(index and user_name) from User_Management_Page to User_Detail_page via Navigator.pushNamed. But I can't know a way to do this.
In User_Management_Page:
Navigator.pushNamed(context,'/user-management/detail',
arguments: {index, user.name},
);
Set up RouterModule:
#override
// TODO: implement routers
List<Router> get routers => [
Router("/usermagement", child: (_, args) => UsersPage()),
Router("/detail", child: (_, args) => UserDetailPage()),
];
And the contractor class User_detail:
class UserDetailPage extends StatelessWidget {
final int index;
final String name;
UserDetailPage({Key key, this.index, this.name}) : super(key: key);
}
This solution is very easy by passing variables in constructor:
first page:
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>SecondPage('something')));
second page:
class SecondPage extends StatefulWidget {
String something;
SecondPage(this.something);
#override
State<StatefulWidget> createState() {
return SecondPageState(this.something);
}
}
class SecondPageState extends State<SecondPage> {
String something;
SecondPageState(this.something);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
//now you have passing variable
title: Text(something),
),
...
}
I'm wondering what the recommended way of passing data to a stateful widget, while creating it, is.
The two styles I've seen are:
class ServerInfo extends StatefulWidget {
Server _server;
ServerInfo(Server server) {
this._server = server;
}
#override
State<StatefulWidget> createState() => new _ServerInfoState(_server);
}
class _ServerInfoState extends State<ServerInfo> {
Server _server;
_ServerInfoState(Server server) {
this._server = server;
}
}
This method keeps a value both in ServerInfo and _ServerInfoState, which seems a bit wasteful.
The other method is to use widget._server:
class ServerInfo extends StatefulWidget {
Server _server;
ServerInfo(Server server) {
this._server = server;
}
#override
State<StatefulWidget> createState() => new _ServerInfoState();
}
class _ServerInfoState extends State<ServerInfo> {
#override
Widget build(BuildContext context) {
widget._server = "10"; // Do something we the server value
return null;
}
}
This seems a bit backwards as the state is no longer stored in _ServerInfoSate but instead in the widget.
Is there a best practice for this?
Don't pass parameters to State using it's constructor.
You should only access the parameters using this.widget.myField.
Not only editing the constructor requires a lot of manual work ; it doesn't bring anything. There's no reason to duplicate all the fields of Widget.
EDIT :
Here's an example:
class ServerIpText extends StatefulWidget {
final String serverIP;
const ServerIpText ({ Key? key, this.serverIP }): super(key: key);
#override
_ServerIpTextState createState() => _ServerIpTextState();
}
class _ServerIpTextState extends State<ServerIpText> {
#override
Widget build(BuildContext context) {
return Text(widget.serverIP);
}
}
class AnotherClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: ServerIpText(serverIP: "127.0.0.1")
);
}
}
Best way is don't pass parameters to State class using it's constructor. You can easily access in State class using widget.myField.
For Example
class UserData extends StatefulWidget {
final String clientName;
final int clientID;
const UserData(this.clientName,this.clientID);
#override
UserDataState createState() => UserDataState();
}
class UserDataState extends State<UserData> {
#override
Widget build(BuildContext context) {
// Here you direct access using widget
return Text(widget.clientName);
}
}
Pass your data when you Navigate screen :
Navigator.of(context).push(MaterialPageRoute(builder: (context) => UserData("WonderClientName",132)));
Another answer, building on #RémiRousselet's anwser and for #user6638204's question, if you want to pass initial values and still be able to update them in the state later:
class MyStateful extends StatefulWidget {
final String foo;
const MyStateful({Key key, this.foo}): super(key: key);
#override
_MyStatefulState createState() => _MyStatefulState(foo: this.foo);
}
class _MyStatefulState extends State<MyStateful> {
String foo;
_MyStatefulState({this.foo});
#override
Widget build(BuildContext context) {
return Text(foo);
}
}
For passing initial values (without passing anything to the constructor)
class MyStateful extends StatefulWidget {
final String foo;
const MyStateful({Key key, this.foo}): super(key: key);
#override
_MyStatefulState createState() => _MyStatefulState();
}
class _MyStatefulState extends State<MyStateful> {
#override
void initState(){
super.initState();
// you can use this.widget.foo here
}
#override
Widget build(BuildContext context) {
return Text(foo);
}
}
Flutter's stateful widgets API is kinda awkward: storing data in Widget in order to access it in build() method which resides in State object 🤦 If you don't want to use some of bigger state management options (Provider, BLoC), use flutter_hooks (https://pub.dev/packages/flutter_hooks) - it is a nicer and cleaner substitute for SatefullWidgets:
class Counter extends HookWidget {
final int _initialCount;
Counter(this._initialCount = 0);
#override
Widget build(BuildContext context) {
final counter = useState(_initialCount);
return GestureDetector(
// automatically triggers a rebuild of Counter widget
onTap: () => counter.value++,
child: Text(counter.value.toString()),
);
}
}
#Rémi Rousselet, #Sanjayrajsinh, #Daksh Shah is also better. but I am also defined this is in from starting point.that which parameter is which value
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
String name = "Flutter Demo";
String description = "This is Demo Application";
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainActivity(
appName: name,
appDescription: description,
),
);
}
}
class MainActivity extends StatefulWidget {
MainActivity({Key key, this.appName, this.appDescription}) : super(key: key);
var appName;
var appDescription;
#override
_MainActivityState createState() => _MainActivityState();
}
class _MainActivityState extends State<MainActivity> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.appName),
),
body: Scaffold(
body: Center(
child: Text(widget.appDescription),
),
),
);
}
}
The best practice is to define the stateful widget class as immutable which means defining all dependencies (arrival parameter) as final parameters. and getting access to them by widget.<fieldName> in the state class. In case you want to change their values like reassigning you should define the same typed properties in your state class and re-assign them in the initState function. it is highly recommended not to define any not-final property in your stateful widget class and make it a mutable class. something like this pattern:
class SomePage extends StatefulWidget{
final String? value;
SomePage({this.value});
#override
State<SomePage> createState() => _SomePageState();
}
class _SomePageState extends State<SomePage> {
String? _value;
#override
void initState(){
super.initState();
setState(() {
_value = widget.value;
});
}
#override
Widget build(BuildContext context) {
return Text(_value);
}
}
To pass data to stateful widget, first of all, create two pages. Now from the first page open the second page and pass the data.
class PageTwo extends StatefulWidget {
final String title;
final String name;
PageTwo ({ this.title, this.name });
#override
PageTwoState createState() => PageTwoState();
}
class PageTwoStateState extends State<PageTwo> {
#override
Widget build(BuildContext context) {
return Text(
widget.title,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w700),
),
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialButton(
text: "Open PageTwo",
onPressed: () {
var destination = ServicePage(
title: '<Page Title>',
provider: '<Page Name>',
);
Navigator.push(context,
MaterialPageRoute(builder: (context) => destination));
},);
}
}
I'm wondering what the recommended way of passing data to a stateful widget, while creating it, is.
The two styles I've seen are:
class ServerInfo extends StatefulWidget {
Server _server;
ServerInfo(Server server) {
this._server = server;
}
#override
State<StatefulWidget> createState() => new _ServerInfoState(_server);
}
class _ServerInfoState extends State<ServerInfo> {
Server _server;
_ServerInfoState(Server server) {
this._server = server;
}
}
This method keeps a value both in ServerInfo and _ServerInfoState, which seems a bit wasteful.
The other method is to use widget._server:
class ServerInfo extends StatefulWidget {
Server _server;
ServerInfo(Server server) {
this._server = server;
}
#override
State<StatefulWidget> createState() => new _ServerInfoState();
}
class _ServerInfoState extends State<ServerInfo> {
#override
Widget build(BuildContext context) {
widget._server = "10"; // Do something we the server value
return null;
}
}
This seems a bit backwards as the state is no longer stored in _ServerInfoSate but instead in the widget.
Is there a best practice for this?
Don't pass parameters to State using it's constructor.
You should only access the parameters using this.widget.myField.
Not only editing the constructor requires a lot of manual work ; it doesn't bring anything. There's no reason to duplicate all the fields of Widget.
EDIT :
Here's an example:
class ServerIpText extends StatefulWidget {
final String serverIP;
const ServerIpText ({ Key? key, this.serverIP }): super(key: key);
#override
_ServerIpTextState createState() => _ServerIpTextState();
}
class _ServerIpTextState extends State<ServerIpText> {
#override
Widget build(BuildContext context) {
return Text(widget.serverIP);
}
}
class AnotherClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: ServerIpText(serverIP: "127.0.0.1")
);
}
}
Best way is don't pass parameters to State class using it's constructor. You can easily access in State class using widget.myField.
For Example
class UserData extends StatefulWidget {
final String clientName;
final int clientID;
const UserData(this.clientName,this.clientID);
#override
UserDataState createState() => UserDataState();
}
class UserDataState extends State<UserData> {
#override
Widget build(BuildContext context) {
// Here you direct access using widget
return Text(widget.clientName);
}
}
Pass your data when you Navigate screen :
Navigator.of(context).push(MaterialPageRoute(builder: (context) => UserData("WonderClientName",132)));
Another answer, building on #RémiRousselet's anwser and for #user6638204's question, if you want to pass initial values and still be able to update them in the state later:
class MyStateful extends StatefulWidget {
final String foo;
const MyStateful({Key key, this.foo}): super(key: key);
#override
_MyStatefulState createState() => _MyStatefulState(foo: this.foo);
}
class _MyStatefulState extends State<MyStateful> {
String foo;
_MyStatefulState({this.foo});
#override
Widget build(BuildContext context) {
return Text(foo);
}
}
For passing initial values (without passing anything to the constructor)
class MyStateful extends StatefulWidget {
final String foo;
const MyStateful({Key key, this.foo}): super(key: key);
#override
_MyStatefulState createState() => _MyStatefulState();
}
class _MyStatefulState extends State<MyStateful> {
#override
void initState(){
super.initState();
// you can use this.widget.foo here
}
#override
Widget build(BuildContext context) {
return Text(foo);
}
}
Flutter's stateful widgets API is kinda awkward: storing data in Widget in order to access it in build() method which resides in State object 🤦 If you don't want to use some of bigger state management options (Provider, BLoC), use flutter_hooks (https://pub.dev/packages/flutter_hooks) - it is a nicer and cleaner substitute for SatefullWidgets:
class Counter extends HookWidget {
final int _initialCount;
Counter(this._initialCount = 0);
#override
Widget build(BuildContext context) {
final counter = useState(_initialCount);
return GestureDetector(
// automatically triggers a rebuild of Counter widget
onTap: () => counter.value++,
child: Text(counter.value.toString()),
);
}
}
#Rémi Rousselet, #Sanjayrajsinh, #Daksh Shah is also better. but I am also defined this is in from starting point.that which parameter is which value
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
String name = "Flutter Demo";
String description = "This is Demo Application";
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainActivity(
appName: name,
appDescription: description,
),
);
}
}
class MainActivity extends StatefulWidget {
MainActivity({Key key, this.appName, this.appDescription}) : super(key: key);
var appName;
var appDescription;
#override
_MainActivityState createState() => _MainActivityState();
}
class _MainActivityState extends State<MainActivity> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.appName),
),
body: Scaffold(
body: Center(
child: Text(widget.appDescription),
),
),
);
}
}
The best practice is to define the stateful widget class as immutable which means defining all dependencies (arrival parameter) as final parameters. and getting access to them by widget.<fieldName> in the state class. In case you want to change their values like reassigning you should define the same typed properties in your state class and re-assign them in the initState function. it is highly recommended not to define any not-final property in your stateful widget class and make it a mutable class. something like this pattern:
class SomePage extends StatefulWidget{
final String? value;
SomePage({this.value});
#override
State<SomePage> createState() => _SomePageState();
}
class _SomePageState extends State<SomePage> {
String? _value;
#override
void initState(){
super.initState();
setState(() {
_value = widget.value;
});
}
#override
Widget build(BuildContext context) {
return Text(_value);
}
}
To pass data to stateful widget, first of all, create two pages. Now from the first page open the second page and pass the data.
class PageTwo extends StatefulWidget {
final String title;
final String name;
PageTwo ({ this.title, this.name });
#override
PageTwoState createState() => PageTwoState();
}
class PageTwoStateState extends State<PageTwo> {
#override
Widget build(BuildContext context) {
return Text(
widget.title,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w700),
),
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialButton(
text: "Open PageTwo",
onPressed: () {
var destination = ServicePage(
title: '<Page Title>',
provider: '<Page Name>',
);
Navigator.push(context,
MaterialPageRoute(builder: (context) => destination));
},);
}
}