Manuel Kammermann
Manuel Kammermann

Reputation: 93

How to pass user to named route with a stateful widget using Navigator in flutter?

My Setup

My Idea

My Problem

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'])
        );
    }
}

Upvotes: 1

Views: 4777

Answers (3)

dat ha minh
dat ha minh

Reputation: 141

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();
    }
}

Upvotes: 1

Manuel Kammermann
Manuel Kammermann

Reputation: 93

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:

Upvotes: 3

Ravinder Kumar
Ravinder Kumar

Reputation: 8010

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);

Upvotes: 0

Related Questions