1

My Setup

  • Login Route - asynchronously checks if a user is logged
  • Checkin Route - Consists of a main stateful widget and several stateless children widgets that depend on the information of the logged in user

My Idea

  • After the user login is identified on the login route, use Navigator to pass along the user object to the checkin route
  • children widgets can receive the user object through their constructor

My Problem

  • the user object is not 'arriving' in the stateful widget of the checkin route

Files

Here are the relevant code snippets to check (I added them from more complex files and hope I got all imports correct):

Main.dart

import 'package:flutter/material.dart';
import 'loginView.dart';
import 'checkinView.dart';

void main() {
    WidgetsFlutterBinding.ensureInitialized();
    runApp(MyApp());
}

class MyApp extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            initialRoute: '/',
            routes: <String, WidgetBuilder>{
                '/': (BuildContext context) => LoginView(),
                '/checkin': (BuildContext context) => CheckinView(),
            }
        );
    }
}

LoginView.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class LoginView extends StatefulWidget {
    @override
    LoginViewState createState() => LoginViewState();
}

class LoginViewState extends State<LoginView> {
    @override
    void initState() {
        super.initState();

        checkLoginAndRedirect();
    }

    void checkLoginAndRedirect() async {
        FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
            if(currUser != null){
                print("[LoginView - user] $currUser");
                Navigator.pushNamed(context, '/checkin', arguments: {currUser});
            }
        });
    }
}

CheckinView.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'checkinList.dart';

class CheckinView extends StatefulWidget {
    final FirebaseUser user;

    CheckinView({this.user});

    @override
    CheckinViewState createState() {
        print("[CheckinView - user] $user");
        return CheckinViewState();
    }
}

class CheckinViewState extends State<CheckinView> {
    FirebaseUser _currentUser;

    @override
    void initState() {
        super.initState();

        setState((){
            _currentUser = widget.user;
        });
    }

    @override
    Widget build(BuildContext context) {

        print("[CheckinViewState - widget.user] " + widget.user.toString());

        return Scaffold(
            body: new Column(
                children: <Widget>[
                    CheckinList(user: _currentUser)
                ]
            )
        );
    }
}

CheckinList.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class CheckinList extends StatelessWidget{
    FirebaseUser user = null;

    CheckinList({this.user = null});

    @override
    Widget build(BuildContext context) {
        return Container(
            padding: EdgeInsets.all(30.0),
            child: Text(user['name'])
        );
    }
}
  • I think this is a duplicate of https://stackoverflow.com/questions/56262655/flutter-get-passed-arguments-from-navigator-in-widgets-states-initstate. I had the same problem. This answer worked. – devdanke Jun 21 '20 at 03:50

3 Answers3

3

Solution

  • The arguments need extraction in the build method of the WidgetState (because only there we have the BuildContext)
  • Navigator is automagically handling the arguments correct, even with Stateful Widgets - THANKS FLUTTER TEAM!!!!

My adapted Files

Main.dart

import 'package:flutter/material.dart';
import 'loginView.dart';
import 'checkinView.dart';

void main() {
    WidgetsFlutterBinding.ensureInitialized();
    runApp(MyApp());
}

class MyApp extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            initialRoute: '/',
            routes: <String, WidgetBuilder>{
                '/': (BuildContext context) => LoginView(),
                '/checkin': (BuildContext context) => CheckinView(),
            }
        );
    }
}

LoginView.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class LoginView extends StatefulWidget {
    @override
    LoginViewState createState() => LoginViewState();
}

class LoginViewState extends State<LoginView> {
    @override
    void initState() {
        super.initState();

        checkLoginAndRedirect();
    }

    void checkLoginAndRedirect() async {
        FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
            if(currUser != null){
                print("[LoginView - user] $currUser");
                //// pass a map as 'arguments' to be ready to pass along more variables in the future
                Navigator.pushNamed(context, '/checkin', arguments: {'user': currUser});
            }
        });
    }
}

CheckinView.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'checkinList.dart';

class CheckinView extends StatefulWidget {
    //// leave the stateful class as usual

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

class CheckinViewState extends State<CheckinView> {
    //// don't bother in the initState method as the BuildContext is missing here

    @override
    Widget build(BuildContext context) {

        //// access the passed arguments
        Map<String, dynamic> args = ModalRoute.of(context).settings.arguments;

        print("[CheckinViewState - widget.user] " + widget.user.toString());

        return Scaffold(
            body: new Column(
                children: <Widget>[
                    //// access the passed variables through 'args'
                    CheckinList(user: args.user)
                ]
            )
        );
    }
}

CheckinList.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class CheckinList extends StatelessWidget{
    FirebaseUser user = null;

    //// assign the user in your constructor
    CheckinList(FirebaseUser user){
        this.user = user;
    };

    @override
    Widget build(BuildContext context) {
        return Container(
            padding: EdgeInsets.all(30.0),
            child: Text(user['name'])
        );
    }
}

Read more in the cookbook about it:

1

You can try it. I hope it can help you

class LoginViewState extends State<LoginView> {
    @override
    void initState() {
        super.initState();

        checkLoginAndRedirect();
    }

    void checkLoginAndRedirect() async {
        FirebaseAuth.instance.currentUser().then((FirebaseUser currUser){
            if(currUser != null){
                print("[LoginView - user] $currUser");
                Navigator.pushNamed(context, '/checkin', arguments: currUser); /// <--- fixed here
            }
        });
    }
}
class CheckinView extends StatefulWidget {
    RouteSettings settings = ModalRoute.of(context).settings /// <--- fixed here
    FirebaseUser user = settings.arguments; /// <--- fixed here

    @override
    CheckinViewState createState() {
        print("[CheckinView - user] $user");
        return CheckinViewState();
    }
}
dat ha minh
  • 141
  • 3
0

You need to access passed data using ModalRoute inside build method, below code will give you can idea how to do it,

class LevelViewChildScreen extends StatelessWidget {
  UserModel user;

  @override
  Widget build(BuildContext context) {
    user = ModalRoute.of(context).settings.arguments;

    return LevelViewChild(user);
  }
}

class LevelViewChild extends StatefulWidget {
  UserModel user;

  LevelViewChild(this.user);
Ravinder Kumar
  • 7,407
  • 3
  • 28
  • 54