Blasanka
Blasanka

Reputation: 22437

Pass StatefulWidget data to the State class without using constructor

I managed to pass Stateful class variables' values to the State class through constructor like below:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Demo',
        home: MyHomePage('John', 'Morison'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage(this.fname, this.lname);

  final String fname;
  final String lname;

  @override
  _MyHomePageState createState() => _MyHomePageState(fname, lname);
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState(this.fname, this.lname);

  final String fname;
  final String lname;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Hello $fname $lname'),
      )
    );
  }
}

That's weird and I had to do lot of work as there is more than two variables. Is there a better way?

Upvotes: 7

Views: 6669

Answers (3)

Rasputin221
Rasputin221

Reputation: 177

There is also a solution with not using the constructor provided in the Flutter Cookbook: https://docs.flutter.dev/cookbook/navigation/passing-data

The idea is using RouteSettings und ModalRoute; here the important code parts:

class _TodosScreenState extends State<TodosScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(

// ...            

    onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const DetailScreen(),
                      // Pass the arguments as part of the RouteSettings. The DetailScreen reads the arguments from these settings.
                      
                      settings: RouteSettings(
                        
                     // widget. is important to access the variable of the stateful class TodosScreen
                        arguments: widget.todos[index],

// ...

class _DetailScreenState extends State<DetailScreen> {
  
  @override
  Widget build(BuildContext context) {
  
    // must be inside build method, as here is the context parameter available 

    final todo = ModalRoute.of(context)!.settings.arguments as Todo;

   // accessing the variable as u wish
   Text(todo.title)

Upvotes: 0

Hossein Sajadinia
Hossein Sajadinia

Reputation: 594

You can access your variables by widget. like widget.myVariable in the state class. however, it's the best practice to pass the variables to the state class constructor if you want to change them in the state class. remember that the variable must be defined as final in the parent (stateful) class, so if you want to change these variables you must define non-final variables in the state class and initiate their values by the constructor.

  class RecordPage extends StatefulWidget {
      final int myvVariable;
      RecordPage({required this.myvVariable});

      @override
      _RecordPageState createState() => new _RecordPageState(myStateVariable: myvVariable);
  }

class _RecordPageState extends State<RecordPage> {
  int myStateVariable;
  _RecordPageState({required this.myStateVariable});
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          "your initiating variable is " + widget.myvVariable.toString(),
        ),
        Text(
          "your state variable is " + myStateVariable.toString(),
        ),
        InkWell(
          onTap: (){
            setState(() {
              myStateVariable++;
            });
          },
          child: new Container(
            child: Center(
              child: new Text(
                "increase the variable",
              ),
            ),
          ),
        )
      ],
    );
  }
}

Upvotes: 1

Blasanka
Blasanka

Reputation: 22437

Yes, there is widget:

From Doc:

  /// The current configuration.
  ///
  /// A [State] object's configuration is the corresponding [StatefulWidget]
  /// instance. This property is initialized by the framework before calling
  /// [initState]. If the parent updates this location in the tree to a new
  /// widget with the same [runtimeType] and [Widget.key] as the current
  /// configuration, the framework will update this property to refer to the new
  /// widget and then call [didUpdateWidget], passing the old configuration as
  /// an argument.
  T get widget => _widget;
  T _widget;

Code should look like below:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Demo',
        home: MyHomePage('John', 'Morison'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage(this.fname, this.lname);

  final String fname;
  final String lname;

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

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Hello ${widget.fname} ${widget.lname}'),
      )
    );
  }
}

Upvotes: 15

Related Questions