Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

dart - How to save last opened screen in flutter app

I am trying to reopen last opened screen after boot, Is there any simple way to do so ? sample codes are welcome !

So far I tried a code(which I got somewhere) with SharedPreferences, but it's not working.

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

String lastRouteKey = 'last_route';

void main() async {
  SharedPreferences preferences = await SharedPreferences.getInstance();
  String lastRoute = preferences.getString(lastRouteKey);
  runApp(MyApp(lastRoute));
}

class MyApp extends StatelessWidget {
  final String lastRoute;

  MyApp(this.lastRoute);

  @override
  Widget build(BuildContext context) {
    bool hasLastRoute = getWidgetByRouteName(lastRoute) != null;

    return MaterialApp(
      home: Foo(),
      initialRoute: hasLastRoute ? lastRoute : '/',
      onGenerateRoute: (RouteSettings route) {
        persistLastRoute(route.name);
        return MaterialPageRoute(
          builder: (context) => getWidgetByRouteName(route.name),
        );
      },
    );
  }

  Widget getWidgetByRouteName(String routeName) {
    switch (routeName) {
      case '/':
        return MainWidget();
      case '/':
        return SecondRoute();
      // Put all your routes here.
       default:
         return null;
    }
  }

  void persistLastRoute(String routeName) async {
    SharedPreferences preferences = await SharedPreferences.getInstance();
    preferences.setString(lastRouteKey, routeName);
  }
}

class Foo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Foo'),
      ),
      body: Column(
        children: <Widget>[
          RaisedButton(
            child: Text('Open route second'),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => SecondRoute()),
                  );
            },
          ),

          RaisedButton(
            child: Text('Open route main'),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => MainWidget()),
              );
            },
          ),
        ],
      ),
    );
  }
}

class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

class MainWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("MainWidget"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

should I use SQLite or JSON instead of SharedPreferences to make the code simple? thanks.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Demo

Demo

A. Navigation

when we are navigating through different screens within app, actually, the route stacks are changing.

So, firstly, we need to figure out how to listen to this changes e.g Push screen, Pop back to users screen.

1. Attaching saving method in each action button

we can actually put this on every navigation-related button.

a. on drawer items

  ListTile(
    title: Text("Beta"),
    onTap: () {
      saveLastScreen(); // saving to SharedPref here
      Navigator.of(context).pushNamed('/beta'); // then push
    },
  ),

b. on Titlebar back buttons

    appBar: AppBar(
    title: Text("Screen"),
    leading: IconButton(
      icon: Icon(Icons.menu),
      onPressed: () {
        saveLastScreen(); // saving to SharedPref here
        Navigator.pop(context); // then pop
      },
    ),
  ),

c. and also capturing event of Phone Back button on Android devices

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: (){ // will triggered as we click back button
        saveLastScreen(); // saving to SharedPref here
        return Future.value(true);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text("Base Screen"),
        ),

Therefore, we will have more code and it will be harder to manage.

2. Listening on Route Changes using Route observer

Nonetheless, Flutter provides on MaterialApp, that we can have some "middleware" to capture those changes on route stacks.

We may have this on our MyApp widget :

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Save Last Route',
      navigatorObservers: <NavigatorObserver>[
        MyRouteObserver(), // this will listen all changes
      ],
      routes: {
        '/': (context) {
          return BaseScreen();
        },
        '/alpha': (context) {
          return ScreenAlpha();
        },

We can define MyRouteObserver class as below :

class MyRouteObserver extends RouteObserver {

  void saveLastRoute(Route lastRoute) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString('last_route', lastRoute.settings.name);
  }

  @override
  void didPop(Route route, Route previousRoute) {
    saveLastRoute(previousRoute); // note : take route name in stacks below
    super.didPop(route, previousRoute);
  }

  @override
  void didPush(Route route, Route previousRoute) {
    saveLastRoute(route); // note : take new route name that just pushed
    super.didPush(route, previousRoute);
  }

  @override
  void didRemove(Route route, Route previousRoute) {
    saveLastRoute(route);
    super.didRemove(route, previousRoute);
  }

  @override
  void didReplace({Route newRoute, Route oldRoute}) {
    saveLastRoute(newRoute);
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
  }
}

B. How to Start the App

As users interacting through the screens, the Shared Preferences will always store last route name. To make the app navigate correspondingly, we need to make our BaseScreen statefull and override its initState method as below :

return MaterialApp(
  routes: {
    '/': (context) {
      return BaseScreen(); // define it as Main Route
    },
class BaseScreen extends StatefulWidget {
  @override
  _BaseScreenState createState() => _BaseScreenState();
}

class _BaseScreenState extends State<BaseScreen> {
  @override
  void initState() {
    super.initState();
    navigateToLastPage();
  }

  void navigateToLastPage() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    String lastRoute = prefs.getString('last_route');
    // No need to push to another screen, if the last route was root
    if (lastRoute.isNotEmpty && lastRoute != '/') {
      Navigator.of(context).pushNamed(lastRoute);
    }
  }

C. Working Repo

You may look at this repository that overrides RouteObserver as explained in second option above

Saving and Opening Screen Beta and Screen Delta in different starts

D. Shared Preferences / JSON / SQLite

I suggest to use Shared preferences for simplicity. As we only record simple String for route name, we can only write two lines of code to Save and two lines of code to Load.

If we use JSON file, we need to manually set Path for it using path_provider package.

Moreover, if we use SQLite, we need to setup DB (may consist > 8 more lines), and setup table and also inserting table method.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...