如何在Flutter应用程序中保存上次打开的屏幕 [英] 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 !
到目前为止,我尝试使用 SharedPreferences
编写一个代码(我到某处了)。
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!'),
),
),
);
}
}
我应该使用 SQLite
或 JSON
代替 SharedPreferences
来简化代码?谢谢。
should I use SQLite
or JSON
instead of SharedPreferences
to make the code simple? thanks.
推荐答案
演示
Demo
实际上,当我们在应用程序内的不同屏幕之间导航时,路线堆栈正在改变。
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.
实际上,我们可以将其放在每个与导航相关的按钮上。
we can actually put this on every navigation-related button.
<强>。抽屉上的物品
ListTile(
title: Text("Beta"),
onTap: () {
saveLastScreen(); // saving to SharedPref here
Navigator.of(context).pushNamed('/beta'); // then push
},
),
b。在标题栏后退按钮上
appBar: AppBar(
title: Text("Screen"),
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {
saveLastScreen(); // saving to SharedPref here
Navigator.pop(context); // then pop
},
),
),
c。并在Android设备上捕获电话返回按钮的事件
@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.
Flutter在MaterialApp上提供了一些 中间件
来捕获路线堆栈上的更改。
Nonetheless, Flutter provides on MaterialApp, that we can have some "middleware
" to capture those changes on route stacks.
我们可能在MyApp小部件上有此内容:
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();
},
我们可以定义 MyRouteObserver
如下所示的类:
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。如何启动应用程序
当用户通过屏幕进行交互时,共享首选项将始终存储最后的路线名称。为了使应用程序进行相应导航,我们需要使BaseScreen处于全状态并覆盖其initState方法,如下所示:
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。工作仓库
您可以查看此存储库如上面第二个选项所述,它将覆盖RouteObserver
C. Working Repo
You may look at this repository that overrides RouteObserver as explained in second option above
在不同的开始位置保存和打开Screen Beta和Screen Delta
Saving and Opening Screen Beta and Screen Delta in different starts
为简单起见,我建议使用共享首选项。因为我们只记录了简单的String作为路由名,所以我们只能将两行代码保存到Save中,并将两行代码写入Load。
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.
如果我们使用JSON文件,我们需要使用 path_provider
包为其手动设置路径。
If we use JSON file, we need to manually set Path for it using path_provider
package.
此外,如果使用SQLite,则需要进行设置数据库(可能包括多于8行),设置表以及插入表的方法。
Moreover, if we use SQLite, we need to setup DB (may consist > 8 more lines), and setup table and also inserting table method.
这篇关于如何在Flutter应用程序中保存上次打开的屏幕的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!