How to perform lazy loading or infinity loading in flutter? - django

I am trying to get lazy loading output with flutter. I could do lazy loading only with a generated array given by flutter as an example. But I couldn't get the same output when integrating with Rest API. How to perform lazy loading with an API in a flutter?

I call an api with pagination ,
here is my code :
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class TestLazyLoading extends StatefulWidget {
#override
_TestLazyLoadingState createState() => new _TestLazyLoadingState();
}
class _TestLazyLoadingState extends State<TestLazyLoading> {
static const String _url = 'https://api.coinranking.com/v1/public/coins';
ScrollController controller;
int _totalCount = 0;
int _limit = 20;
int _offset = 0;
List<String> items = [];
bool _isLoading = true;
#override
void initState() {
super.initState();
controller = new ScrollController()..addListener(_scrollListener);
_getData(limit: _limit, offset: _offset);
}
#override
void dispose() {
controller.removeListener(_scrollListener);
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Scrollbar(
child: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
if (items.length-1 == index && _isLoading ) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Container(
child: Text(items[index]),
height: 38,
alignment: Alignment.centerLeft,
margin: EdgeInsets.all(5),
padding: EdgeInsets.all(10),
color: Colors.grey[200],
);
}
},
itemCount: items.length,
),
),
),
);
}
void _scrollListener() {
if (controller.position.extentAfter < 50) {
if (!_isLoading && _totalCount > items.length) {
_offset += _limit;
_getData(limit: _limit, offset: _offset);
}
}
}
void _getData({#required int limit, #required int offset}) async {
setState(() {
_isLoading = true;
});
http.Response response =
await http.get('$_url?limit=$limit&offset=$offset');
if (response.statusCode == 200) {
var jsonResponse = jsonDecode(response.body);
_totalCount = jsonResponse['data']['stats']['total'];
List<dynamic> coinList = jsonResponse['data']['coins'];
for (dynamic coin in coinList) {
items.add(coin['symbol']);
}
setState(() {
_isLoading = false;
});
}
}
}
also add dependncy for http in you pubspec.yaml :
dev_dependencies:
flutter_test:
sdk: flutter
http: ^0.12.1
finally call TestLazyLoading widget in the main for testing

Related

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 !

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';

Flutter dynamically update date and time

I am new to flutter and this is my first app. I am trying to make a to do list app and want to display the time left for each task in the subtitle. I have a listview and in each element I want the have the subtitle display the minute, counting downwards towards 0. Can anyone help me with this ? Thanks!
Code : -
class toDoListState extends State<toDoList>
{
List<String> tasks = [];
List<String> completedTasks = [];
List<String> descriptions = [];
List<bool> importance = [];
List<String> time2completion = [];
List<DateTime> time = [];
Widget buildToDoList()
{
return new ListView.builder
(
itemBuilder: (context, index)
{
if(time2completion.length > 0 && index < time2completion.length && time2completion[index] != "none")
{
if(time2completion[index] == "30 minutes")
{
time[index] = DateTime.now().add(Duration(minutes: 30));
}
else if(time2completion[index] == "1 hour")
{
time[index] = DateTime.now().add(Duration(hours: 1));
}
else if(time2completion[index] == "12 hours")
{
time[index] = DateTime.now().add(Duration(hours: 12));
}
else if(time2completion[index] == "1 day")
{
time[index] = DateTime.now().add(Duration(days: 1));
}
}
if(index < tasks.length)
{
return row(tasks[index], descriptions[index], index);
}
},
);
}
Widget row(String task, String description, int index)
{
return Dismissible(
key: UniqueKey(),
background: Container(color: Colors.red, child: Align(alignment: Alignment.center, child: Text('DELETE', textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 18),))),
direction: DismissDirection.horizontal,
onDismissed: (direction) {
setState(() {
tasks.removeAt(index);
if(completedTasks.contains(task))
{
completedTasks.removeAt(index);
}
descriptions.removeAt(index);
importance.removeAt(index);
});
Scaffold.of(context).showSnackBar(SnackBar(content: Text(task+" dismissed")));
},
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(task, style: (completedTasks.contains(task)) ? TextStyle(decoration: TextDecoration.lineThrough) : TextStyle(),),
subtitle: Text((time[index].difference(DateTime.now()).toString())),
value: completedTasks.contains(task),
onChanged: (bool value) {
setState(() {
if(!completedTasks.contains(task))
{
completedTasks.add(task);
}
else
{
completedTasks.remove(task);
}
});
},
));
}
}
You can use a timer to calculate the time differences every minute.
Timer.periodic(
Duration(minutes: 1),
(Timer t) => setState(() {
// your calculation here
}),
);
The following will create a timer object in your stateful widget and dispose of it when you navigate away from the view:
Timer _timer;
#override
void initState() {
_timer = Timer.periodic(
Duration(minutes: 1),
(Timer t) => setState(() {
// your calculation here
}),
);
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
You can achieve this by following:
Suppose you already have saved date in your todo and have already a date available in
form of DateTime object here in my example I am assuming savedDateTime which can be achieved either by assigning it using .hour , .sec or parsing it from string.
Now what you do is to find what time is left that is differnce
//already assumed saved date as a DateTime object that is savedDateTime
// it contains our saved date of note
//now
final currentDateTime = DateTime.now();
final difference = currentDateTime.difference(savedDateTime);
difference in Seconds,Hours,Minutes,Days is given by
print(difference.inSeconds);
print(difference.inHours);
print(difference.inMinutes);
print(difference.inDays);

Cache two Lists in flutter

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.

How to send a List object through a callback function in Flutter?

What I've already done
I have a stateful widget that generates a ListView on screen.
class AppListView extends StatefulWidget {
final ValueChanged onChange;
final List<MatchList> matchList;
final ValueChanged finalBetList;
AppListView({this.onChange, this.matchList, this.finalBetList});
#override
_AppListViewState createState() => _AppListViewState();
}
class _AppListViewState extends State<AppListView> {
int counter = 0;
List<MatchList> betList = List<MatchList>();
I have a Home Screen that calls this Stateful Widget. In here I am using a callback function (onChange) to get the counter value from the widget. Everything is working perfect.
Stack(children: [
AppListView(
matchList: matchList,
//callback function brings the counter value from ListView class
onChange: (value) {
setState(() {
counter = value;
});
},
),
Positioned(
child: Padding(
padding: const EdgeInsets.only(top: 280.0, left: 330.0),
child: Container(
width: 60,
height: 60,
child: FloatingActionButton(
onPressed: () {
counter > 0
? Navigator.pushNamed(context, BetScreen.id)
: print('you shall not pass');
},
child: Text(
counter.toString(),
style: kTextStyleAppBarTitle,
),
),
),
),
)
])
What is the problem
But when I am trying to call that widget from another screen with a similar callback function(finalBetList), I got "The method 'call' was called on null. Receiver: null" error. Actually everything that I do is the same as the other example that works fine. I can't find what I'm missing. Is it something about Lists?
class _BetScreenState extends State<BetScreen> {
List<MatchList> betList = List<MatchList>();
int counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
currentBalance: '\u0024' + '200.00',
userLevel: 'Level 30',
userName: 'Mark',
),
body: Container(
child: AppListView(
finalBetList: (value) {
setState(() {
//counter = value;
betList = value;
//print(betList);
});
},
matchList: betList,
),
),
);
}
}
After Edit
I added the cod of AppListView widget
class AppListView extends StatefulWidget {
final ValueChanged onChange;
final List<MatchList> matchList;
final ValueChanged finalBetList;
AppListView({this.onChange, this.matchList, this.finalBetList});
#override
_AppListViewState createState() => _AppListViewState();
}
class _AppListViewState extends State<AppListView> {
int counter = 0;
List<int> betList = List<int>();
#override
Widget build(BuildContext context) {
children: <Widget>[
AppButton.buildAppButton(
context,
AppButtonType.BETSELECTION,
widget.matchList[index].homeOdds,
kCategoryButtonDimensions,
color: widget.matchList[index].homeSelected
? Colors.yellow
: Colors.white,
onPressed: () {
if (widget.matchList[index].drawSelected ||
widget.matchList[index].awaySelected) {
widget.matchList[index].drawSelected =
false;
widget.matchList[index].awaySelected =
false;
counter--;
//betList part
if (betList.length > 0)
betList
.remove(widget.matchList[index].id);
}
widget.matchList[index].homeSelected =
!widget.matchList[index].homeSelected;
if (widget.matchList[index].homeSelected) {
counter++;
betList.add(widget.matchList[index].id);
} else {
counter--;
if (betList.length > 0)
betList.remove(widget.matchList[index]
.id); //if selected, add to betList to send BetScreen
}
widget.onChange(counter);
print(betList);
widget.finalBetList(betList);
setState(() {});
},
),