So, I'm trying to group sort a list by a particular method of an class. My code currently stores "meals" with Hive by this class format:
https://pub.dev/packages/hive
import 'package:hive/hive.dart';
part 'meal.g.dart';
#HiveType(typeId: 1, adapterName: "MealAdapter")
class MealModel{
#HiveField(0)
int id;
#HiveField(1)
String mealName;
#HiveField(2)
int calories;
#HiveField(3)
int carbohydrates;
#HiveField(4)
int protein;
#HiveField(5)
int fats;
#HiveField(6)
String day;
MealModel(this.id, this.mealName, this.calories, this.day, [this.carbohydrates, this.protein, this.fats]);
}
The problem is when I have no idea I would pass values to each of the parameters for a GroupedListView (https://pub.dev/packages/grouped_list) to display the stored meals:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:jaetrack/colors.dart';
import 'package:jaetrack/dbmodel/meal.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:grouped_list/grouped_list.dart';
class Meals extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MealsState();
}
}
class MealsState extends State<Meals> {
Box<MealModel> mealBox = Hive.box<MealModel>('meals');
MealModel previousMeal;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: mainPurp,
body: SingleChildScrollView(
child: Column(
children: [
ValueListenableBuilder(
valueListenable: Hive.box<MealModel>('meals').listenable(),
builder: (context, Box<MealModel> box, _) {
final mealList = box.values.toList().cast<MealModel>();
print(mealList);
return GroupedListView<dynamic, String>(
elements: mealList,
groupBy: (meal) => meal, // no idea how to sort by calories, name etc. of my MealModel class,
groupSeparatorBuilder: (String groupByValue) =>
Text(groupByValue), // date value
itemBuilder: (context, dynamic element) =>
Text(element['name']), // this is where I would put the meal names/calories etc.
itemComparator: (item1, item2) =>
item1['name'].compareTo(item2['name']), // sort by date is what I'm looking for but again I don't know how to access
useStickyGroupSeparators: true,
floatingHeader: true,
order: GroupedListOrder.ASC,
);
})
],
),
),
);
}
}
So if I still don't make sense, I cast the values of my hive box of meals as instances of the MealModel above. So the list would be like [Instance of MealModel, Instance of MealModel, ...] I just need to know how to access the values inside of the instance of the MealModel to pass to the GroupedList.
package:grouped_list library are using dynamic in all of their examples to simplify usage for people who don't get generics. but it's misleading in your case, you're losing the strong typing.
cast to
return GroupedListView<MealModel, String>(
instead of
return GroupedListView<dynamic, String>(
then all of your element, meal and item2 vars will be of MealModel and you can do element.mealName, and meal.calories to group by calories
for the sorting question, simply sort mealList before passing it to the GroupedListView
Related
I'm trying to create a graph and i don't know how to set values on this graph (I know how to set the values manually but i created a field in firestore that receives the value of x and y of the graph when you click on a button ), i'm using SyncFusion Flutter Chart (if you have any another idea to solve my problem feel free to say) and i'm trying to add the values ( String ) to the list and for that i Created in firestore when you click on the button (ChartData(x,y)) so all i need is add this string to the list of type ChartData, and for that i'm trying to use decode and split but now returned this error and i don't know how to solve. i'm sorry for the long text.
List<ChartData> convert(String input) {
List<ChartData> output;
try {
output = json.decode(input);
return output;
} catch (err) {
print('The input is not a string representation of a list');
return [ChartData(0, 0)];
}
}
List<ChartData> testim = testefinal.split(',');
final List<ChartData> list1 = convert(testefinal);
final List<ChartData> chartData = list1;
Error: A value of type 'List<String>' can't be assigned to a variable of type 'List<ChartData>'.
Full Code:
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:mood3/telas/animacao.dart';
import 'package:ntp/ntp.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import '../model/GraphMood.dart';
class Graph extends StatefulWidget {
#override
State<Graph> createState() => _GraphState();
}
class _GraphState extends State<Graph> {
TooltipBehavior? _tooltipBehavior;
DateTime ntpTime = DateTime.now();
String testefinal = '';
_loadNTPTime() async {
FirebaseAuth auth = FirebaseAuth.instance;
FirebaseFirestore db = FirebaseFirestore.instance;
setState(() async {
ntpTime = await NTP.now();
});
}
Future _recuperarNome() async {
FirebaseAuth auth2 = FirebaseAuth.instance;
User? user = auth2.currentUser;
DocumentSnapshot ds = await db.collection('usuarios')
.doc(user!.uid)
.get();
Map<String, dynamic> dss = ds.data() as Map<String, dynamic>;
setState((){
testefinal = dss["moodGraph"];
});
print(testefinal);
}
FirebaseFirestore db = FirebaseFirestore.instance;
#override
initState(){
_recuperarNome();
_tooltipBehavior = TooltipBehavior(enable: true);
super.initState();
_loadNTPTime();
}
#override
Widget build(BuildContext context) {
List<ChartData> convert(String input) {
List<ChartData> output;
try {
output = json.decode(input);
return output;
} catch (err) {
print('The input is not a string representation of a list');
return [ChartData(0, 0)];
}
}
List<ChartData> testim = testefinal.split(',');
final List<ChartData> list1 = convert(testefinal);
final List<ChartData> chartData = list1;
return Scaffold(
body: Center(
child: Container(
child: SfCartesianChart(
primaryXAxis: CategoryAxis(),
// Chart title
title: ChartTitle(text: 'Mood Chart'),
// Enable legend
legend: Legend(isVisible: true),
// Enable tooltip
tooltipBehavior: _tooltipBehavior,
series: <ChartSeries>[
// Renders line chart
LineSeries<ChartData, int>(
dataSource: chartData,
xValueMapper: (ChartData data, _) => data.x.toInt(),
yValueMapper: (ChartData data, _) => data.y
)
]
)
)
)
);
}
}
class ChartData {
ChartData(this.x, this.y);
final double x;
final double y;
}
I think, output = json.decode(input) as List();
I have a list of items i want if i clicked on any of them his bool favorite equal true using shared pref..
in more details I Have a list of objects with a bool favorite attribute. I Want to store this boolean value to shared pref..
Model Class:
class ZekrModel {
final String zekrTitle;
final String zekrImage;
final String zekrCat;
final Widget screenWidget;
bool isFav;
bool toggleDone() {
isFav = !isFav;
}
}
Provider Class:
class ZekrProvider with ChangeNotifier{
List<ZekrModel> _zekrList = [
ZekrModel(
zekrTitle: 'أذكار المساء',
zekrImage: 'assets/images/sunset.png',
zekrCat: 'Azkar',
screenWidget: AlmasaaScreen(),
),
ZekrModel(
zekrTitle: 'أذكار الصباح',
zekrImage: 'assets/images/sunrise.png',
zekrCat: 'Azkar',
screenWidget: AlsabahScreen(),
)
];
void updateFav(ZekrModel zekrModel) {
zekrModel.toggleDone();
notifyListeners();
}
List<ZekrModel> get favZekr {
return _zekrList.where((element) => element.isFav).toList();
}
}
Usage in UI:
onTap: (){
value.updateFav(zekrIndex);
},
Import the package:
import 'package:shared_preferences/shared_preferences.dart';
Your widget:
class _LoginState extends State<Login> {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Then, just call like that inside any method to set a bool:
final SharedPreferences prefs = await _prefs;
prefs.setBool('logado', true);
prefs.setString('logado_email', people.email);
If you want to retrieve it later:
bool status = prefs.getBool('logado') ?? false;
The situation:
I'm very new to Flutter and mobile development, thus I don't know much about Dart; And I've read some solutions from people with similar problems but didn't manage to work these solutions to my own thing.
The problem:
I have a to-do app that has 2 Lists of Objects, and I want to store those Lists for whenever the user re-open the app.
I know its simple stuff but I feel like I'm storming towards this problem due to the lack of experience... And so I decided to come asking for some light.
What I've tried:
I have come across different solutions for this problem and all of them seemed way too complex to this case (compared to what I'm used to do when saving lists to the archive), including: encoding the list to a map and converting to a string before using SharedPreferences, using SQlite database (every tutorial I've come across made me feel like I'd be using a war tank to kill an ant, I'd say the same about firebase).
Structure of the problem:
ToDo screen with a ListView.builder calling 2 arrays: ongoing tasks and done tasks each of which I want to write to the phone whenever the user makes a change. IDK if I should only try to save those arrays from within the class from which they belong by calling some packages methods, or if I should try to store the entire application if such thing is possible.
Conclusion:
Is there a way to solve this in a simple way or I should use something robust like firebase just for that? even though I'm not used to work with firestore, and so I'm in the dark not knowing how to apply such thing to save data.
How my lists are structured:
List<Task> _tasks = [
Task(
name: "do something",
description: "try to do it fast!!!",
),
];
List<Task> _doneTasks = [
Task(
name: "task marked as done",
description: "something",
),
];
2022 Update with null safety
My original code example was more verbose than necessary. Using Darts factory constructor this can be done with way less code. This is also updated for null safety and using Hive instead of GetStorage.
First, add a toMap method which converts the object to a Map, then a fromMap constructor which returns a Task object from a Map that was saved in storage.
class Task {
final String name;
final String description;
Task({required this.name, required this.description});
Map<String, dynamic> toMap() {
return {'name': this.name, 'description': this.description};
}
factory Task.fromMap(Map map) {
return Task(
name: map['name'],
description: map['description'],
);
}
String toString() {
return 'name: $name description: $description';
}
}
Updated Demo Page
class StorageDemo extends StatefulWidget {
#override
_StorageDemoState createState() => _StorageDemoState();
}
class _StorageDemoState extends State<StorageDemo> {
List<Task> _tasks = [];
final box = Hive.box('taskBox');
// separate list for storing maps/ restoreTask function
//populates _tasks from this list on initState
List storageList = [];
void addAndStoreTask(Task task) {
_tasks.add(task);
storageList.add(task.toMap()); // adding temp map to storageList
box.put('tasks', storageList); // adding list of maps to storage
}
void restoreTasks() {
storageList = box.get('tasks') ?? []; // initializing list from storage
// looping through the storage list to parse out Task objects from maps
for (final map in storageList) {
_tasks
.add(Task.fromMap(map)); // adding Tasks back to your normal Task list
}
}
// looping through your list to see whats inside
void printTasks() {
for (final task in _tasks) {
log(task.toString());
}
}
void clearTasks() {
_tasks.clear();
storageList.clear();
box.clear();
}
#override
void initState() {
super.initState();
restoreTasks(); // restore list from storing in initState
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Container(),
),
TextButton(
onPressed: () {
final task =
Task(description: 'test description', name: 'test name');
addAndStoreTask(task);
},
child: Text('Add Task'),
),
TextButton(
onPressed: () {
printTasks();
},
child: Text('Print Storage'),
),
TextButton(
onPressed: () {
clearTasks();
},
child: Text('Clear Tasks'),
),
],
),
);
}
}
Updated Storage Init
void main() async {
await Hive.initFlutter();
await Hive.openBox('taskBox');
runApp(MyApp());
}
Original Answer
So generally speaking, once you want to store anything other than a primitive type ie. String int etc... things get a bit more complex because they have to converted to something that's readable by any storage solution.
So despite Tasks being a basic object with a couple strings, SharedPreferences or anything else doesn't know what a Task is or what to do with it.
I suggest in general reading about json serialization, as you'll need to know about it either way. This is a good place to start and here is another good article about it.
All that being said, it can also be done without json by converting your task to a Map (which is what json serialization does anyway) and storing it to a list of maps. I'll show you an example of doing this manually without json. But again, its in your best interest to buckle down and spend some time learning it.
This example will use Get Storage, which is like SharedPreferences but easier because you don't need separate methods for different data types, just read and write.
I don't know how you're adding tasks in your app, but this is just a basic example of storing a list of Task objects. Any solution that doesn't involve online storage requires storing locally, and retrieving from storage on app start.
So let's say here is your Task object.
class Task {
final String name;
final String description;
Task({this.name, this.description});
}
Put this in your main method before running your app
await GetStorage.init();
You'll need to add async to your main, so if you're not familiar with how that works it looks like this.
void main() async {
await GetStorage.init();
runApp(MyApp());
}
Normally I would NEVER do all this logic inside a stateful widget, but instead implement a state management solution and do it in a class outside of the UI, but that's a whole different discussion. I also recommend checking out GetX, Riverpod, or Provider reading about them and seeing which one strikes you as the easiest to learn. GetX gets my vote for simplicity and functionality.
But since you're just starting out I'll omit that part of it and just put all these functions in the UI page for now.
Also instead of only storing when app closes, which can also be done, its easier to just store anytime there is a change to the list.
Here's a page with some buttons to add, clear, and print storage so you can see exactly whats in your list after app restart.
If you understand whats going on here you should be able to do this in your app, or study up on json and do it that way. Either way, you need to wrap your head around Maps and how local storage works with any of the available solutions.
class StorageDemo extends StatefulWidget {
#override
_StorageDemoState createState() => _StorageDemoState();
}
class _StorageDemoState extends State<StorageDemo> {
List<Task> _tasks = [];
final box = GetStorage(); // list of maps gets stored here
// separate list for storing maps/ restoreTask function
//populates _tasks from this list on initState
List storageList = [];
void addAndStoreTask(Task task) {
_tasks.add(task);
final storageMap = {}; // temporary map that gets added to storage
final index = _tasks.length; // for unique map keys
final nameKey = 'name$index';
final descriptionKey = 'description$index';
// adding task properties to temporary map
storageMap[nameKey] = task.name;
storageMap[descriptionKey] = task.description;
storageList.add(storageMap); // adding temp map to storageList
box.write('tasks', storageList); // adding list of maps to storage
}
void restoreTasks() {
storageList = box.read('tasks'); // initializing list from storage
String nameKey, descriptionKey;
// looping through the storage list to parse out Task objects from maps
for (int i = 0; i < storageList.length; i++) {
final map = storageList[i];
// index for retreival keys accounting for index starting at 0
final index = i + 1;
nameKey = 'name$index';
descriptionKey = 'description$index';
// recreating Task objects from storage
final task = Task(name: map[nameKey], description: map[descriptionKey]);
_tasks.add(task); // adding Tasks back to your normal Task list
}
}
// looping through you list to see whats inside
void printTasks() {
for (int i = 0; i < _tasks.length; i++) {
debugPrint(
'Task ${i + 1} name ${_tasks[i].name} description: ${_tasks[i].description}');
}
}
void clearTasks() {
_tasks.clear();
storageList.clear();
box.erase();
}
#override
void initState() {
super.initState();
restoreTasks(); // restore list from storing in initState
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Container(),
),
TextButton(
onPressed: () {
final task =
Task(description: 'test description', name: 'test name');
addAndStoreTask(task);
},
child: Text('Add Task'),
),
TextButton(
onPressed: () {
printTasks();
},
child: Text('Print Storage'),
),
TextButton(
onPressed: () {
clearTasks();
},
child: Text('Clear Tasks'),
),
],
),
);
}
}
Welcome to Flutter and Dart! Things might seem daunting in the start, but rewarding later on. Think of it logically, how is data to be stored outside the state of the app?
It has to be fetched from some data storage, this can either from the devices storage, or from a remote database.
For the device storage, you have the two options you mentioned, but SQlite would be an overkill for this task, however sharedPreferences isn't daunting as it seems, and very easy to use once you get the hang of it.
It's basically storing data on the device in the form of a string, along with a uniquekey to retrieve that data later on. This key is also a string.
For your app, you only need two keys: 'tasks' & 'completedTasks'.
Where are you facing trouble? encoding the data as a string? Or converting it to a map first then encoding it?
You're other option would be a remote database, that you send data over the internet, Firebase is only one of the possible solutions, besides building your own server, API and database, which is definitely an overkill for this situation as well.
However, since you are still getting the hang of things, this would be a good project to start with shared preferences, then in phase two, look into firebase, so your ToDo list items can be synced across multiple devices. This also comes with the added benefit you will gain from learning firebase, I strongly advise you look into it.
Every new language is daunting in the start, but expecting things to be 1+1=2 form the start will not get you where you want to be, and I'm certain you did not start learning Flutter to only make todo apps, but what you learn from this app will prepare you for what the future holds.
My advise, get comfortable with being uncomfortable, and remember why you started this journey, and whatever help you need, the community will never disappoint, just meet us or meet yourself half way.
I'm new to flutter and have created a simple app to display some custom List Tiles. In each tile, it's possible to increase or decrease the count by the + and - buttons.
But I want to get these data into a map, on the click of the button "Order". For example, my map would be like {'shoes': 1, 'books': 2, 'water': 3} etc.
I've tried Provider, StreamBuilder, BLoC etc. But since I'm a beginner, I couldn't get a real use of any of those.
Could any of you please tell me how to get this data into a map.
Thanks for reading.
You can define a Map like this:
List<String> products = ['shoes', 'books', 'water'];
Map<String,int> map = Map.fromIterables(products , 0);
When user press - or + buttons in each tile, update your equvalent <key, value> in your map.
For example in onTap event of shoes's + button you have
map.update('shoes', (v)=> v+1);
Then you can use updated map in onTap of Order button.
For more info about Map in dart visit this link.
With "bloc", you need to build your events, states in the Bloc would be something like that:
The event will be where the Bloc is triggered.
part of 'something_bloc.dart';
#immutable
abstract class SomethingEvent{}
class ListOfSomething extends SomethingEvent{}
class SomethingElse extends SomethingEvent{
final String input;
SomethingElse({this.input});
List<Object> get props => [input];
}
The state is where the result of what was triggered in the event will be manifested, whether successful or unsuccessful:
part of 'Something_bloc.dart';
#immutable
abstract class SomethingState{}
class SomethingInitial extends SomethingState{}
class SomethingLoading extends SomethingState{}
class SomethingSuccess extends SomethingState{
final String success;
SomethingSuccess({this.success});
}
The "bloc" is where to manage these events and states:
part 'something_event.dart';
part 'something_state.dart';
class SomethingBloc extends Bloc<SomethingEvent,SomethingState>{
final ServiceFacade service;
SomethingBloc({this.service});
#override
SomethingState get initialState => SomethingInitial();
#override
Stream<SomethingState> mapEventToState(SomethingEvent event) async* {
if(event is SomethingEvent){
final failureOrSuccess = await service.call(event.cardId);
yield failureOrSuccess.fold(
(failure) => (failure is GeneralFailure)
? SomethingErrorState(error: failure.message)
: null,
(list) => SomethingState(success: list)
);
}
}
}
The screen will look something like this, you can trigger the event in an OnPressed, or initialize it in the initState():
BlocConsumer<SomethingBloc, SomethingState>(
listener: (context, state) {
if (state is SomethingLoadingState) {
} else if (state is SomethingErrorState) {
} else if (state is SomethingSuccess) {
}
},
builder: (context, state) {
if (state is SomethingSuccess) {
return Padding(
padding: paddingHorizontal,
child: Column(
children: <Widget>[
for (var item in state.list)
Container(
child: Text('${item}')
)
]
),
);
} else {
return Container();
}
},
),
I hope I've helped in general terms.
I want to store a list of objects into shared preferences, then get back the list from it.
I have tried to store a list of objects but I'm getting an error like cannot
encoding the object.
var list = jsonEncode(Building.getInstance().childList.toString());
pref.putString(SharedKey().CHILD_LIST,list);
I expected to get the list of objects as I have stored in shared preferences.
You can convert your complex object list to json string and save with setStringList
And use getStringList to get json string and convert it back to your complex object list
full code demo use setStringList and getStringList
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(new MaterialApp(
home: new Scaffold(
body: new Center(
child: new RaisedButton(
onPressed: _save,
child: new Text('Save my list of int'),
),
),
),
));
}
_save() async {
List<String> myListOfStrings= ["1","2","3","4"];
List<String> myListOfJsonStrings= ['{ "storeid":"123", "storename":"jock"}',' { "storeid":"456", "storename":"abc"}' ];
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setStringList('mylist', myListOfStrings);
List<String> myList = (prefs.getStringList('mylist') ?? List<String>()) ;
print('Your list $myList');
await prefs.setStringList('myjsonlist', myListOfJsonStrings);
List<String> myjsonList = (prefs.getStringList('myjsonlist') ?? List<String>()) ;
print('Your list $myjsonList');
}
Convert it to a string, you can store it
import 'dart:convert';
...
var s = json.encode(myMap);
// or var s = jsonEncode(myMap);
json.decode()/jsonDecode() makes a map from a string when you load it.