Passing data to StatefulWidget and accessing it in its state in Flutter
Asked Answered
S

10

355

I have 2 screens in my Flutter app: a list of records and a screen for creating and editing records.

If I pass an object to the second screen that means I am going to edit this and if I pass null it means that I am creating a new item. The editing screen is a Stateful widget and I am not sure how to use this approach https://flutter.io/cookbook/navigation/passing-data/ for my case.

class RecordPage extends StatefulWidget {
  final Record recordObject;

  RecordPage({Key key, @required this.recordObject}) : super(key: key);

  @override
  _RecordPageState createState() => new _RecordPageState();
}

class _RecordPageState extends State<RecordPage> {
  @override
  Widget build(BuildContext context) {
   //.....
  }
}

How can I access recordObject inside _RecordPageState?

Screens answered 11/5, 2018 at 8:16 Comment(4)
Possible duplicate of Passing Data to a Stateful WidgetAlberthaalberti
how can we use 'recordObject' variable value in a function of _RecordPageState class?Epiphany
Does this answer your question? Pass StatefulWidget data to the State class without using constructorForan
All the answers except the last one made by G. Debailly are wrong. Never use access to context.widget to retrieve what-you-assume-is-parent's data. For the parent may be quite unexpected one. E.g. in ListView it might be a SliverChildBuilderDelegate. Indead follow G. Debailly advice and learn Simple app state management article.Dub
A
516

To use recordObject in _RecordPageState, you have to just write widget.objectname like below

class _RecordPageState extends State<RecordPage> {
  @override
  Widget build(BuildContext context) {
   .....
   widget.recordObject
   .....
  }
}
Affairs answered 11/5, 2018 at 9:18 Comment(8)
Those who are new to Flutter, do not forget to define widget like '@override RecordPage get widget => super.widget;'Treadway
@Treadway Why is that necessary?Alberthaalberti
Should't the recordObject be part of the State class? Logically, having it in StatefulWidget is incorrect (in terms of cohesion). Besides, all fields of StatefulWidget should be immutable - what if you want to change the recordObject reference?Enucleate
I have exactly the same and it's not working doing something like Text(widget.recordObject), it says that my var is nullDeka
how can we use 'recordObject' variable value in a function of _RecordPageState class?Epiphany
It does not seem necessary to define the widget as in @hhk's comment anymore.Madras
What if I want to change the field in RecordPage at runtime? Is pass the field to State class as an constructor argument is a better way?Snowclad
Sorry, but this is wrong. See my comment to question.Dub
A
129

Full Example

You don't need to pass parameters to State using it's constructor. You can easily access these using widget.myField.

class MyRecord extends StatefulWidget {
  final String recordName;
  const MyRecord(this.recordName);

  @override
  MyRecordState createState() => MyRecordState();
}

class MyRecordState extends State<MyRecord> {
  @override
  Widget build(BuildContext context) {
    return Text(widget.recordName); // Here you direct access using widget
  }
}

Pass your data when you Navigate screen :

 Navigator.of(context).push(MaterialPageRoute(builder: (context) => MyRecord("WonderWorld")));
Aceae answered 8/11, 2019 at 13:47 Comment(2)
This is already mentioned here many years ago. Please read other answers before writing yours.Thaumatrope
What if I need change its value?Carin
D
52
class RecordPage extends StatefulWidget {
  final Record recordObject;

  RecordPage({Key key, @required this.recordObject}) : super(key: key);

  @override
  _RecordPageState createState() => new _RecordPageState(recordObject);
}

class _RecordPageState extends State<RecordPage> {
  Record  recordObject
 _RecordPageState(this. recordObject);  //constructor
  @override
  Widget build(BuildContext context) {.    //closure has access
   //.....
  }
}
Dystrophy answered 4/2, 2019 at 0:1 Comment(4)
Please explain why this is a statefull widget.Smitten
@Smitten because OP initial script is a StatefulWidget, he just added some lines to fit the needs.Maureen
I do not think that having recordObject field both in State and StatefulWidget classes is such a good idea (even though I saw tutorials doing exactly this). The approach of accessing fields of StatefulWidget by using widget field of State class seem like a more correct approach (even though it has its own problems)Enucleate
This implementation gives no_logic_in_create_state lint warningHydrography
S
15

example as below:

class nhaphangle extends StatefulWidget {
  final String username;
  final List<String> dshangle;// = ["1","2"];
  const nhaphangle({ Key key, @required this.username,@required this.dshangle }) : super(key: key);


  @override
  _nhaphangleState createState() => _nhaphangleState();
}

class _nhaphangleState extends State<nhaphangle> {
  TextEditingController mspController = TextEditingController();
  TextEditingController soluongController = TextEditingController();
  final scrollDirection = Axis.vertical;
  DateTime Ngaysx  = DateTime.now();
  ScrollController _scrollController = new ScrollController();

  ApiService _apiService;
  List<String> titles = [];

  @override
  void initState() {
    super.initState();
    _apiService = ApiService();
    titles = widget.dshangle;  //here var is call and set to 
  }

    
Slotnick answered 20/4, 2021 at 6:32 Comment(1)
Thanks! I was looking for where i can modify the values on the second page.Bismuth
P
9

You don't have to create any new object or variable for this reason in the state class, you need to define just a final and nullable variable for your object in the stateful class. you can access this variable by widget. like widget.recordObject

class RecordPage extends StatefulWidget {
   final Record? recordObject;
   RecordPage({required this.recordObject});
   @override
   _RecordPageState createState() => new _RecordPageState();
}

class _RecordPageState extends State<RecordPage> {
   @override
   Widget build(BuildContext context) {
      return Text(
         widget.recordObject == null? "the object is null" : "the object isn't null",
   );
 }
}
Princess answered 2/4, 2023 at 13:23 Comment(0)
G
4

I have to Navigate back to any one of the screens in the list pages but when I did that my onTap function stops working and navigation stops.

class MyBar extends StatefulWidget {
  MyBar({this.pageNumber});
  final pageNumber;
  static const String id = 'mybar_screen';
  @override
  _MyBarState createState() => _MyBarState();
}

class _MyBarState extends State<MyBar> {
  final List pages = [
    NotificationScreen(),
    AppointmentScreen(),
    RequestBloodScreen(),
    ProfileScreen(),
  ];
  @override
  Widget build(BuildContext context) {
    var _selectedItemIndex = widget.pageNumber;
    return Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          elevation: 0,
          backgroundColor: Colors.white,
          unselectedItemColor: Colors.grey.shade700,
          selectedItemColor: Color(kAppColor),
          selectedIconTheme: IconThemeData(color: Color(kAppColor)),
          currentIndex: _selectedItemIndex,
          type: BottomNavigationBarType.fixed,
          onTap: (int index) {
            setState(() {
              _selectedItemIndex = index;
            });
          },
Gilmer answered 7/11, 2020 at 3:48 Comment(0)
W
1

In my app, often instead of using stateful widgets, I use mainly ChangeNotifierProvider<T> in main.dart, some model class

class FooModel extends ChangeNotifier {

var _foo = false;

void changeFooState() {
   _foo = true;
   notifyListeners();
}

bool getFoo () => _foo;

}

and

var foo = context.read<FooModel>();
# or
var foo = context.watch<FooModel>();

in my stateless widgets. IMO this gives me more precise control over the rebuilding upon runtime state change, compared to stateful widgets.

The recipe can be found in the official docs, the concept is called "lifting state up".

Willi answered 2/8, 2021 at 13:29 Comment(0)
D
0

You should use a Pub/Sub mechanism. I prefer to use Rx in many situations and languages. For Dart/Flutter this is the package: https://pub.dev/packages/rxdart

For example, you can use a BehaviorSubject to emit data from widget A, pass the stream to widget B which listens for changes and applies them inside the setState.

Widget A:

// initialize subject and put it into the Widget B
BehaviorSubject<LiveOutput> subject = BehaviorSubject();
late WidgetB widgetB = WidgetB(deviceOutput: subject);

// when you have to emit new data
subject.add(deviceOutput);

Widget B:

// add stream at class level
class WidgetB extends StatefulWidget {
   final ValueStream<LiveOutput> deviceOutput;
   const WidgetB({Key? key, required this.deviceOutput}) : super(key: key);

   @override
   State<WidgetB> createState() => _WidgetBState();
}

// listen for changes
@override
void initState() {
   super.initState();

   widget.deviceOutput.listen((event) {
      print("new live output");
      setState(() {
         // do whatever you want
      });
   });
}
Dannica answered 1/7, 2022 at 15:27 Comment(0)
E
0

This code will clear your doubt :-

class RecordPage extends StatefulWidget {
  final Record recordObject;

  RecordPage({Key key, @required this.recordObject}) : super(key: key);

  @override
  _RecordPageState createState() => new _RecordPageState();
}

class _RecordPageState extends State<RecordPage> {
  @override
  Widget build(BuildContext context) {
   return Text(widget.record)
  }
}
Endodontist answered 5/2 at 16:48 Comment(0)
H
0

You can override State class from initState() if you need to set initial value to your state variables from the StatefulWidget


class _RecordPageState extends State<RecordPage> {

  Record _recordObject;

  @override
  void initState() {
    super.initState();
    _recordObject = widget.recordObject;
  }


  @override
  Widget build(BuildContext context) {
   //.....
  }
}
Hydrography answered 17/3 at 6:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.