save navigation

This commit is contained in:
Oksana Pokrovskaya 2018-03-21 17:29:21 +03:00
parent 9015862f1a
commit cbbd95ab23
10 changed files with 244 additions and 42 deletions

View File

@ -12,29 +12,31 @@ import android.os.Parcelable
class MainActivity() : FlutterActivity() {
var savedModels: MutableMap<String, String> = mutableMapOf()
var savedFromFlutter: MutableMap<String, String> = mutableMapOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler { call, result ->
if (call.method.contentEquals("saveModel")) {
savedModels.put(call.argument<String>("key"), call.argument<String>("value"))
} else if (call.method.contentEquals("readModel")) {
result.success(savedModels.get(call.argument<String>("key")))
if (call.method.contentEquals("saveInput")) {
savedFromFlutter.put(call.argument<String>("key"), call.argument<String>("value"))
} else if (call.method.contentEquals("readInput")) {
result.success(savedFromFlutter.get(call.argument<String>("key")))
} else if (call.method.contentEquals("wasRestarted")) {
result.success(savedInstanceState != null)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable("savedModels", toBundle(savedModels));
outState.putParcelable("savedFromFlutter", toBundle(savedFromFlutter));
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
savedModels = fromBundle(savedInstanceState.getParcelable<Bundle>("savedModels"))
savedFromFlutter = fromBundle(savedInstanceState.getParcelable<Bundle>("savedFromFlutter"))
}
}

View File

@ -1,8 +1,44 @@
import 'dart:collection';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_app/random_words.dart';
import 'dart:async';
void main() => runApp(new MyApp());
class Keys {
static GlobalKey<RandomWordsState> key = new GlobalKey<RandomWordsState>();
}
class Routes {
static Queue<String> routes = new Queue<String>();
static var _firstTime = true;
static save() async {
const platform = const MethodChannel('app.channel.shared.data');
platform.invokeMethod(
"saveInput", {"key": "routes", "value": JSON.encode(routes.toList())});
}
static restore(BuildContext context) async {
if (!_firstTime) {
return;
}
const platform = const MethodChannel('app.channel.shared.data');
String s = await platform.invokeMethod("readInput", {"key": "routes"});
if (s != null) {
routes = new Queue<String>();
routes.addAll(JSON.decode(s));
}
_firstTime = false;
for (String route in routes) {
Navigator.of(context).pushNamed(route);
}
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
@ -11,7 +47,11 @@ class MyApp extends StatelessWidget {
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
home: new MyHomePage(title: 'Startup Name Generator'),
routes: <String, WidgetBuilder>{
'/saved': (BuildContext context) =>
new SavedPage(title: 'Saved Suggestions'),
},
);
}
}
@ -29,11 +69,70 @@ class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
Routes.restore(context);
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
],
),
body: new RandomWords("list"),
body: new RandomWords(Keys.key, "list"),
);
}
}
void _pushSaved() async {
Routes.routes.addLast('/saved');
await Routes.save();
Navigator.of(context).pushNamed('/saved');
}
}
class SavedPage extends StatefulWidget {
SavedPage({Key key, this.title}) : super(key: key);
final String title;
@override
_SavedPageState createState() => new _SavedPageState();
}
class _SavedPageState extends State<SavedPage> {
@override
Widget build(BuildContext context) {
if (!Keys.key.currentState.model.isInitialized()) {
return new Container();
}
final tiles = Keys.key.currentState.model.saved.map(
(pair) {
return new ListTile(
title: new Text(
pair,
style: Keys.key.currentState.biggerFont,
),
);
},
);
final divided = ListTile
.divideTiles(
context: context,
tiles: tiles,
)
.toList();
return new Scaffold(
appBar: new AppBar(
title: new Text('Saved Suggestions'),
),
body: new WillPopScope(
onWillPop: _onWillPop,
child: new ListView(children: divided),),
);
}
Future<bool> _onWillPop() async {
Routes.routes.removeLast();
await Routes.save();
return true;
}
}

View File

@ -1,7 +1,7 @@
import 'dart:async';
abstract class Model {
abstract class Restorable {
save(String key);
Future<Model> restore(String key);
Future<Restorable> restore(String key);
isInitialized();
}

View File

@ -1,34 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/random_words_input.dart';
import 'package:flutter_app/random_words_model.dart';
import 'package:english_words/english_words.dart';
class RandomWords extends StatefulWidget {
final String modelKey;
final String stateKey;
RandomWords(this.modelKey);
RandomWords(Key key, this.stateKey) :super(key: key);
@override
createState() => new RandomWordsState(modelKey);
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
String modelKey;
RandomWordsModel model = new RandomWordsModel();
RandomWordsInput input = new RandomWordsInput();
final _biggerFont = const TextStyle(fontSize: 18.0);
final biggerFont = const TextStyle(fontSize: 18.0);
final ScrollController scrollController = new ScrollController();
RandomWordsState(String stateKey) {
this.modelKey = stateKey;
RandomWordsState() {
_init();
}
_init() async {
RandomWordsModel newModel = await model.restore(modelKey);
RandomWordsModel newModel = await model.restore(widget.stateKey);
RandomWordsInput newInput = await input.restore(widget.stateKey);
setState(() {
model = newModel;
scrollController.jumpTo(model.scrollPosition);
input = newInput;
scrollController.jumpTo(input.scrollPosition);
});
}
@ -60,7 +62,7 @@ class RandomWordsState extends State<RandomWords> {
for (WordPair pair in newSuggestions) {
model.suggestions.add(pair.asPascalCase);
}
model.save(modelKey);
model.save(widget.stateKey);
}
return _buildRow(model.suggestions[index]);
@ -69,16 +71,31 @@ class RandomWordsState extends State<RandomWords> {
}
_onNotification(Notification n) {
model.scrollPosition = scrollController.position.pixels;
model.save(modelKey);
input.scrollPosition = scrollController.position.pixels;
input.save(widget.stateKey);
}
Widget _buildRow(String word) {
final alreadySaved = model.saved.contains(word);
return new ListTile(
title: new Text(
word,
style: _biggerFont,
style: biggerFont,
),
trailing: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
model.saved.remove(word);
} else {
model.saved.add(word);
}
});
model.save(widget.stateKey);
},
);
}
}

View File

@ -0,0 +1,44 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_app/model.dart';
import 'package:json_annotation/json_annotation.dart';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
part 'random_words_input.g.dart';
@JsonSerializable()
class RandomWordsInput extends Object with _$RandomWordsInputSerializerMixin implements Restorable {
double scrollPosition = -1.0;
RandomWordsInput();
factory RandomWordsInput.fromJson(Map<String, dynamic> json) => _$RandomWordsInputFromJson(json);
save(String key) async {
String json = JSON.encode(this);
const platform = const MethodChannel('app.channel.shared.data');
platform.invokeMethod("saveInput", {"key": key, "value": json});
}
Future<RandomWordsInput> restore(String key) async {
const platform = const MethodChannel('app.channel.shared.data');
String s = await platform.invokeMethod("readInput", {"key" : key});
if (s != null) {
var restoredModel = new RandomWordsInput.fromJson(JSON.decode(s));
scrollPosition = restoredModel.scrollPosition;
} else {
_empty();
}
return this;
}
_empty() {
scrollPosition = 0.0;
}
bool isInitialized() {
return scrollPosition >= 0;
}
}

View File

@ -0,0 +1,17 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'random_words_input.dart';
// **************************************************************************
// Generator: JsonSerializableGenerator
// **************************************************************************
RandomWordsInput _$RandomWordsInputFromJson(Map<String, dynamic> json) =>
new RandomWordsInput()
..scrollPosition = (json['scrollPosition'] as num)?.toDouble();
abstract class _$RandomWordsInputSerializerMixin {
double get scrollPosition;
Map<String, dynamic> toJson() =>
<String, dynamic>{'scrollPosition': scrollPosition};
}

View File

@ -1,15 +1,16 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_app/model.dart';
import 'package:json_annotation/json_annotation.dart';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
part 'random_words_model.g.dart';
@JsonSerializable()
class RandomWordsModel extends Object with _$RandomWordsModelSerializerMixin implements Model {
class RandomWordsModel extends Object with _$RandomWordsModelSerializerMixin implements Restorable {
var suggestions;
double scrollPosition = 0.0;
var saved;
RandomWordsModel();
@ -17,24 +18,38 @@ class RandomWordsModel extends Object with _$RandomWordsModelSerializerMixin imp
save(String key) async {
String json = JSON.encode(this);
const platform = const MethodChannel('app.channel.shared.data');
platform.invokeMethod("saveModel", {"key": key, "value": json});
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(key, json);
print(suggestions);
}
Future<RandomWordsModel> restore(String key) async {
const platform = const MethodChannel('app.channel.shared.data');
String s = await platform.invokeMethod("readModel", {"key": key});
bool wasRestarted = await platform.invokeMethod("wasRestarted");
if (!wasRestarted) {
_empty();
return this;
}
SharedPreferences prefs = await SharedPreferences.getInstance();
String s = prefs.getString(key);
if (s != null) {
var restoredModel = new RandomWordsModel.fromJson(JSON.decode(s));
suggestions = restoredModel.suggestions;
scrollPosition = restoredModel.scrollPosition;
saved = restoredModel.saved;
} else {
suggestions = <String>[];
_empty();
}
return this;
}
_empty() {
suggestions = <String>[];
saved = <String>[];
}
bool isInitialized() {
return suggestions != null;
}

View File

@ -8,14 +8,13 @@ part of 'random_words_model.dart';
RandomWordsModel _$RandomWordsModelFromJson(Map<String, dynamic> json) =>
new RandomWordsModel()
..suggestions = json['suggestions']
..scrollPosition = (json['scrollPosition'] as num)?.toDouble();
..suggestions =
(json['suggestions'] as List)?.map((e) => e as String)?.toList()
..saved = (json['saved'] as List)?.map((e) => e as String)?.toList();
abstract class _$RandomWordsModelSerializerMixin {
dynamic get suggestions;
double get scrollPosition;
Map<String, dynamic> toJson() => <String, dynamic>{
'suggestions': suggestions,
'scrollPosition': scrollPosition
};
List<String> get suggestions;
List<String> get saved;
Map<String, dynamic> toJson() =>
<String, dynamic>{'suggestions': suggestions, 'saved': saved};
}

View File

@ -354,6 +354,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.28.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
shelf:
dependency: transitive
description:
@ -500,4 +507,5 @@ packages:
source: hosted
version: "2.1.13"
sdks:
dart: ">=2.0.0-dev.23.0 <=2.0.0-edge.0d5cf900b021bf5c9fa593ffa12b15bcd1cc5fe0"
dart: ">=2.0.0-dev.28.0 <=2.0.0-edge.0d5cf900b021bf5c9fa593ffa12b15bcd1cc5fe0"
flutter: ">=0.1.4 <2.0.0"

View File

@ -11,6 +11,7 @@ dependencies:
english_words: ^3.1.0
build_runner: ^0.7.6
json_serializable: ^0.3.2
shared_preferences: ^0.4.0
dev_dependencies:
flutter_test: