Cache two Lists in flutter - list

I want to cache two lists that got from Firebase to use to later when the user is offline
This is the full code for my list display screen -
import 'package:flutter/material.dart';
import 'package:naamaa/calculations/name-list-calc.dart';
List namesList = List();
List meaningsList = List();
class NameList extends StatefulWidget {
#override
_NameListState createState() => _NameListState();
}
class _NameListState extends State<NameList> {
Future<String> getPosts() async {
var names = await NameListCalc().nameListCalc();
namesList.addAll(names[0]);
meaningsList.addAll(names[1]);
String s = 'test';
return s;
}
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: getPosts(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scaffold(
resizeToAvoidBottomPadding: false,
body: ListView.builder(
padding: EdgeInsets.zero,
itemBuilder: (context, position) {
return Row(
children: <Widget>[
Container(
width: 100,
child: Text(namesList[position]),
),
Container(
child: Text(meaningsList[position]),
)
],
);
},
itemCount: namesList.length,
),
);
} else {
return Text(':(');
}
},
);
}
}
I want to cache namesList and meaningsList for later use.
If someone can help it would be great :)

I didn't get complete requirement by your question description but you can use shared_preferences library to store the data list as following
Add following line pubspec.yaml
dependencies:
flutter:
sdk: flutter
shared_preferences:
You can use this example and add more utility methods as per you requirement.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
AppConfig.init(() {
runApp(MyApp());
});
}
class CustomModel {
int id;
String name;
CustomModel({this.id, this.name});
factory CustomModel.fromJson(Map<String, dynamic> json) {
return CustomModel(id: json["id"], name: json["name"]);
}
Map<String, dynamic> toJson() => {"id": id, "name": name};
#override
String toString() {
return "id: $id, name: $name";
}
}
class AppConfig {
static Future init(VoidCallback callback) async {
WidgetsFlutterBinding.ensureInitialized();
await SharedPreferenceUtils.init();
callback();
}
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class SharedPreferenceUtils {
static SharedPreferences prefs;
static init() async {
prefs = await SharedPreferences.getInstance();
// storing lists
await putStringList("m_list", ["abc", "def"]);
await putObjectList("data",
[CustomModel(id: 1, name: "Bob"), CustomModel(id: 2, name: "Alice")]);
}
static Future<bool> putStringList(String key, List<String> list) async {
return prefs.setStringList(key, list);
}
static List<String> getStringList(String key) {
return prefs.getStringList(key);
}
static Future<bool> putObjectList(String key, List<Object> list) async {
if (prefs == null) return null;
List<String> _dataList = list?.map((value) {
return json.encode(value);
})?.toList();
return prefs.setStringList(key, _dataList);
}
static List<T> getObjList<T>(String key, T f(Map v),
{List<T> defValue = const []}) {
if (prefs == null) return null;
List<Map> dataList = getObjectList(key);
List<T> list = dataList?.map((value) {
return f(value);
})?.toList();
return list ?? defValue;
}
static List<Map> getObjectList(String key) {
if (prefs == null) return null;
List<String> dataList = prefs.getStringList(key);
return dataList?.map((value) {
Map _dataMap = json.decode(value);
return _dataMap;
})?.toList();
}
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(SharedPreferenceUtils.getStringList("m_list").toString()),
Text(SharedPreferenceUtils.getObjList<CustomModel>(
"data", (v) => CustomModel.fromJson(v)).toString()),
],
),
),
),
);
}
}
You don't need to store the lists in init() as it's done for this example. You can also pass data from one widget to others in multiple ways and if you are looking for state management then you can use BLOC or providers.

Related

How can I get more than one Doc in a Collection of a List of Strings? (Flutter)

My question is if I can put a List in a Doc from Firebase.
Here is my Code:
Future getPosts() async {
var test = await FirebaseFirestore.instance
.collection("Profiles")
.doc(auth.currentUser.displayName)
.collection("Following")
.doc()
.get()
.asStream()
.toList();
print(test);
qn = await FirebaseFirestore.instance
.collection("Posts")
.doc() // here I want the string of All documents from the List
.collection("_Posts")
.get();
}
The Code is pretty shitty but yeah hope you can Help.
Here is a working code implementing both FutureBuilder() and StreamBuilder():
class PlayGround extends StatefulWidget {
const PlayGround({Key? key}) : super(key: key);
#override
_PlayGroundState createState() => _PlayGroundState();
}
class _PlayGroundState extends State<PlayGround> {
FirebaseFirestore _db = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
//using a Future ( Fetch once )
Expanded(
child: FutureBuilder<List<PostsModel>>(
future: fetchUserPosts('someUID'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return LinearProgressIndicator();
}
return ListView.builder(itemBuilder: (context, index) {
return Container(
child: Text(snapshot.data![index].title.toString()));
});
},
)),
//using a stream (Real Time)
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: _db
.collection('Users')
.doc('someUID')
.collection('Posts')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return LinearProgressIndicator();
}
List<PostsModel> _postsStream = snapshot.data!.docs
.map((e) =>
PostsModel.fromMap(e.data() as Map<String, dynamic>))
.toList();
return ListView.builder(itemBuilder: (context, index) {
return Container(
child: Text(_postsStream[index].title.toString()));
});
},
)),
],
),
);
}
//to get list of posts from Firestore
Future<List<PostsModel>> fetchUserPosts(userID) async {
var result = await _db
.collection('Users')
.doc(userID)
.collection('Posts')
// .orderBy("SomeThing")
.get();
List<PostsModel> postsModel =
result.docs.map((doc) => PostsModel.fromMap(doc.data())).toList();
return postsModel;
}
}
class PostsModel {
final String? title;
final num? likes;
PostsModel({
this.title,
this.likes,
});
factory PostsModel.fromMap(Map<String, dynamic> json) => PostsModel(
title: json["title"],
likes: json["likes"],
);
Map<String, dynamic> toMap() => {
"title": title,
"likes": likes,
};
}

How can I use the shared_preferences package to save my string list?

I'm trying to save and read a list called "teams" as a shared_preference so every time I switch back to this screen and take a look at my teams list it isn't empty and shows the old values. No matter how I set it up it doesn't seem to work. Then I come back the list is empty. Do you guys have any ideas?
Here is my code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TeamScreen extends StatefulWidget {
#override
_TeamScreenState createState() => _TeamScreenState();
}
class _TeamScreenState extends State<TeamScreen> {
List<String> teams = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: teams.length,
itemBuilder: (context, index) {
return Team(
teams[index],
() => removeTeam(teams[index]),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => newTeam(),
child: Icon(
CupertinoIcons.add,
),
),
);
}
void addTeam(String name) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
teams.add(name);
});
Navigator.of(context).pop();
prefs.setStringList('teams', teams);
}
void newTeam() {
showDialog<AlertDialog>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Name auswählen: '),
content: TextField(
onSubmitted: addTeam,
),
);
},
);
}
void removeTeam(String name) {
setState(() {
teams.remove(name);
});
}
}
class Team extends StatelessWidget {
final String name;
final Function remove;
const Team(this.name, this.remove);
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 22),
child: ListTile(
leading: Icon(Icons.sports_volleyball_outlined),
contentPadding: EdgeInsets.symmetric(vertical: 8.0),
title: Text(
name,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w600,
),
),
trailing: IconButton(
icon: Icon(CupertinoIcons.delete),
onPressed: () => remove(),
),
),
);
}
}
Your code seems almost perfect! just you didn't initialized your teams variable with the SharedPreferences in initState.
lets fix that :
Define a prefs variable
class _TeamScreenState extends State<TeamScreen> {
List<String> teams = [];
late SharedPreferences prefs; //Declare your prefs variable here but with late initializer.
...
Check if teams list is stored in local -> fetch it or if not -> create it with empty list.
void initState() {
super.initState();
tryListFetch(); // defined async function
}
void tryListFetch() async {
prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('teams')) {
prefs.setStringList('teams', []); // created empty teams list on local storage
print('On device data is not available.');
return;
}
print('data avaialble');
teams = prefs.getStringList('teams') as List<String>;
}
Update your local data whenever you make changes in teams variable :
prefs.setStringList('teams', teams);
like in your removeTeam function :
void removeTeam(String name) {
setState(() {
teams.remove(name);
});
prefs.setStringList('teams', teams); //updated local storage's list
}
And in your addTeam function :
void addTeam(String name) async {
// SharedPreferences prefs = await SharedPreferences.getInstance(); //no need to initialize it here as we have already initialized it globally!
setState(() {
teams.add(name);
});
Navigator.of(context).pop();
prefs.setStringList('teams', teams);
}
Done !

Query elements of a Map<String, List<String>> in dart for search delegate

I have a Complex map on Dart with the following structure
{
"India":["Mumbai","Delhi"],
"Australia":["Sydney","Perth" ,"Queensland" ],
"USA":["LA","New York"]
}
I am implementing a search using search delegate in flutter. But not able to query the data using Edit Text.
I am trying to search via City in-country in the above example. how can I do this?
For Example if user type "M" then city with M letter should be shown with country name
Thank you so much.
You just need to iterate over Map, then the value of List like this :
Map<String, List<String>> dataMap = {}; // your data map above
String query; // your search query
List<String> list = []; // list to store the results
if (query.isNotEmpty) {
dataMap.forEach((country, cities) {
cities.forEach((city) {
if (city.toLowerCase().startsWith(query.toLowerCase())) {
list.add('$city, $country');
}
});
});
}
Here the working example :
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return OutlinedButton.icon(
icon: Icon(Icons.search),
label: Text('Search'),
onPressed: () async {
final result = await showSearch(
context: context,
delegate: CustomSearchDelegate(),
);
print(result);
},
);
}
}
class CustomSearchDelegate<String> extends SearchDelegate<String> {
final dataMap = {
"India": [
"Mumbai",
"Delhi",
],
"Australia": [
"Sydney",
"Perth",
"Queensland",
],
"USA": [
"LA",
"New York",
]
};
#override
List<Widget> buildActions(BuildContext context) {
return [
if (query.isNotEmpty)
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
#override
Widget buildLeading(BuildContext context) {
return BackButton(
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
return buildSuggestions(context);
}
#override
Widget buildSuggestions(BuildContext context) {
List<String> list = [];
if (query.isNotEmpty) {
dataMap.forEach((country, cities) {
cities.forEach((city) {
if (city.toLowerCase().startsWith(query.toLowerCase())) {
list.add('$city, $country');
}
});
});
}
return ListView(
children: ListTile.divideTiles(
context: context,
tiles: list.map((data) {
return ListTile(
title: Text(data),
onTap: () {
close(context, data);
},
);
}),
).toList(),
);
}
}

Flutter how to load Future list in a ListView?

Hi how i can Load this list in a ListView or ListViebuilder?
Future<List<bool>> getBoolList() async{
List<bool> prefList = [];
var sharedPreferences = await SharedPreferences.getInstance();
Set<String> keys = sharedPreferences.getKeys();
for(int i=0; i<keys.length ; i++){
bool value = sharedPreferences.getBool(keys.elementAt(i));
prefList.add(value);
}
return prefList;
}
List<bool> list = await getBoolList();
how I got there
Flutter SharedPreferences how to load all saved?
Edit: my favorite.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
// ignore: must_be_immutable
class Favoriten extends StatefulWidget {
#override
_FavoritenState createState() => _FavoritenState();
}
class _FavoritenState extends State<Favoriten> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Favorites'),
),
body: // MyList
);
}
}
You have to use stream builder. It observes the stream. (Best way to implement is using bloc pattern)
class Favoriten extends StatefulWidget {
#override
_FavoritenState createState() => _FavoritenState();
}
class _FavoritenState extends State<Favoriten> {
final _boolList = PublishSubject<List<bool>>();
Observable<List<bool>> get boolList => _boolList.stream;
loadList() async{
List<bool> prefList = [];
var sharedPreferences = await SharedPreferences.getInstance();
Set<String> keys = sharedPreferences.getKeys();
for(int i=0; i<keys.length ; i++){
bool value = sharedPreferences.getBool(keys.elementAt(i));
prefList.add(value);
}
_boolList.sink.add(prefList);
}
#override
Widget build(BuildContext context) {
loadList();
return StreamBuilder(
stream: boolList,
builder: (context, snapshot) {
if (snapshot.hasData) {
return root(snapshot.data);
} else {
return Container(
child: YourLoader(),// display loader
);
}
}
);
}
Widget root(List<bool> list){
return ListView.builder(
itemBuilder: (context, index) {
return Container(); // your design here
}
itemCount: list.length,
);
}
}
Note :- You have to add rxdart: ^0.22.0 plugin in your pubspec.yaml
and then import 'package:rxdart/rxdart.dart';

Not creating a List in flutter

Here is my point: I have two pages. The first page has a textFormField and it's value I send to the second screen. In the second screen I want to create a list of what I received from the first page. But the problem is that the list is never created. It just shows the last element received.
import 'package:flutter/material.dart';
class PageTwo extends StatefulWidget {
final String descricao;
const PageTwo({Key key, this.descricao}) : super(key: key);
#override
_PageTwoState createState() => _PageTwoState();
}
class _PageTwoState extends State<PageTwo> {
final List<String> listDescricao = [];
#override
void initState() {
listDescricao.add(widget.descricao);
super.initState();
}
//List images items
Widget imgListItem() {
return ListView.builder(
itemCount: listDescricao.length,
itemBuilder: (BuildContext context, int index) {
return widget.descricao == null
? Container()
: Text(listDescricao[index]);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: imgListItem(),
);
}
}
As required this is the PAGE ONE:
import 'package:flutter/material.dart';
class PageOne extends StatefulWidget {
#override
_PageOneState createState() => _PageOneState();
}
class _PageOneState extends State<PageOne> {
final GlobalKey<ScaffoldState> _scaffoldkey = new GlobalKey<ScaffoldState>();
final inputDescricaoController = TextEditingController();
//send image
doSend() async {
String descricao = inputDescricaoController.text ??= "";
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) => PageTwo(descricao: descricao)),
(Route<dynamic> route) => false);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: RaisedButton(
child: Column(
children: <Widget>[
TextField(
controller: inputDescricaoController,
),
Text(
'send',
),
],
),
onPressed: () async {
doSend();
},
),
);
}
}