Create A pdf doc with multiple line - pdf

I have the following code for creating a pdf file.
import 'package: flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:http/http.dart' show get;
class PDF extends StatefulWidget {
PDF({Key? key}) : super(key: key);
#override
State<PDF> createState() => _PDFState();
}
class _PDFState extends State<PDF> {
final name = ["d", "d", "3"];
#override
Widget build(BuildContext context) {
return Container(
child: ElevatedButton(
onPressed: () async {
await pdfInvoiceDownload(
"title",
"body",
[name.toString()],
[],
);
},
child: Text('click')),
);
}
}
Future pdfInvoiceDownload(
String title,
String body,
List<String>? name,
List<String>? emails,
) async {
// Add your function code h re!
final pdf = pw.Document();
pdf.addPage(
pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.SizedBox(
height: 1 * PdfPageFormat.cm,
),
pw.Column(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
pw.Text(title),
pw.Text(body),
],
),
pw.SizedBox(
height: 1 * PdfPageFormat.cm,
),
],
),
pw.Text(
"test",
style: const pw.TextStyle(
// color: PdfColors.cyan
fontSize: 24,
),
),
pw.SizedBox(height: 0.8 * PdfPageFormat.cm),
pw.Text("description"),
pw.Table.fromTextArray(
headers: ['name', 'hours', 'Rhours'],
data: <List<dynamic>>[name!.toList()],
headerStyle: pw.TextStyle(fontWeight: pw.FontWeight.bold),
headerDecoration:
const pw.BoxDecoration(color: PdfColors.grey300),
border: null,
cellHeight: 1,
cellAlignments: {
0: pw.Alignment.centerLeft,
1: pw.Alignment.center,
2: pw.Alignment.centerRight,
3: pw.Alignment.centerRight,
4: pw.Alignment.centerRight,
},
),
pw.Divider(),
pw.Container(
alignment: pw.Alignment.centerRight,
child: pw.Row(
children: [
pw.Spacer(
flex: 6,
),
pw.Expanded(
flex: 4,
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Container(
child: pw.Row(
children: [
pw.Expanded(
child: pw.Container(
child: pw.Row(
children: [
// padding: const pw.EdgeInsets.all(12),
pw.Padding(
padding: pw.EdgeInsets.fromLTRB(
0, 0, 15, 0),
child: pw.Text("tootot",
style: pw.TextStyle(
fontWeight: pw.FontWeight.bold,
)),
),
pw.Text("1231313")
],
),
))
],
),
)
]))
],
),
),
pw.Divider(),
],
);
},
),
); // Page
await Printing.layoutPdf(
onLayout: (
PdfPageFormat format,
) async =>
pdf.save());
}
as you can see from the code i have a list called name, my question how can create a row for each element in the list
my current results that the whole data in the list are generated in one row, please i would be apricatedenter image description here if you could help me

Related

Flutter Multiple Checkbox From API

I have been able to display Json data from API in FutureBuilder Widget. However, the widget has checkbox for each list. Whenever I check on one list, the whole list get checked.
I want a help on how to check each list individually and be able to use the data of the selected list.
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:bottom_navy_bar/bottom_navy_bar.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mar/Components/mydrawer.dart';
import 'package:mar/Services/auth.dart';
import 'package:mar/sccreens/readRequirements.dart';
import 'package:mar/sccreens/scanAnalysis.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../constants.dart';
import 'infoAnalysis.dart';
import 'login_screen.dart';
class Know extends StatefulWidget {
static String id = 'Know';
#override
_KnowState createState() => _KnowState();
}
class _KnowState extends State<Know> {
List sympotms = [];
int currentIndex = 2;
bool valuefirst = false;
int _bottomBarIndex = 0;
Auth _auth;
showdialogall(context, String mytitle, String mycontent) {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
title: Text(
mytitle,
style: TextStyle(color: Colors.deepOrange),
),
content: Text(mycontent),
actions: [
Center(
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
color: kMainColor,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ReadReq()),
);
},
child: Text(
"Requirements",
style: TextStyle(color: Colors.black),
)),
),
],
);
});
}
Future fetchdata() async {
var res = await http.get("http://10.0.2.2/medical/symptoms.php");
if (res.statusCode == 200) {
var obj = json.decode(res.body);
return obj;
}
}
bool isSearching = false;
#override
void initState() {
fetchdata().then((data) {
setState(() {
sympotms = data;
});
});
super.initState();
}
void _filterSymptoms(value) {
setState(() {
filteredsympotms = sympotms
.where(
(sym) => sym['name'].toLowerCase().contains(value.toLowerCase()))
.toList();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: kMainColor,
title: Text(
"Know Your Analysis",
style: TextStyle(
fontFamily: 'Pacifico',
),
),
actions: <Widget>[
FlatButton(
child: CircleAvatar(
child: Image(
image: AssetImage('images/icons/medical.png'),
),
backgroundColor: Colors.black,
),
onPressed: () {
Navigator.pushNamed(context, Scan.id);
},
),
],
centerTitle: true,
),
drawer: MyDrawer(),
body: Column(
children: [
Expanded(
child: sympotms.length > 0
? ListView.builder(
itemBuilder: (_, index) {
return Container(
child: Row(
children: [
SizedBox(width: 10),
Checkbox(
value: this.valuefirst,
onChanged: (bool value) {
setState(() {
valuefirst = value;
});
},
checkColor: Colors.greenAccent,
activeColor: Colors.black,
),
Text(
"${sympotms[index]['SymptomsName']}",
style: TextStyle(fontSize: 17.0),
),
],
),
);
},
itemCount: sympotms.length,
)
: Container(child: Center(child: Text("Loading..."))),
),
RaisedButton(
child: Text(
" Submit ",
style: TextStyle(fontSize: 20),
),
onPressed: () {
showdialogall(context, "Result !", "CBC Test");
},
// onPressed: showdialogall(context, "Result !", "CBC Test"),
color: Colors.green,
textColor: Colors.white,
splashColor: Colors.grey,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
),
],
),
//Navigation
bottomNavigationBar: BottomNavyBar(
animationDuration: Duration(milliseconds: 800),
curve: Curves.easeInOut,
selectedIndex: currentIndex,
onItemSelected: (index) async {
if (index == 0) {
Navigator.pushNamed(context, Scan.id);
}
if (index == 1) {
Navigator.pushNamed(context, Information.id);
}
if (index == 2) {
Navigator.pushNamed(context, Know.id);
}
if (index == 3) {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.clear();
await _auth.signOut();
Navigator.popAndPushNamed(context, LoginScreen.id);
}
setState(() {
currentIndex = index;
});
},
items: <BottomNavyBarItem>[
BottomNavyBarItem(
icon: Icon(
Icons.camera_alt,
),
title: Text('Scan'),
activeColor: Colors.black,
inactiveColor: Colors.black,
),
BottomNavyBarItem(
icon: Icon(
Icons.perm_device_information,
),
title: Text('Information'),
activeColor: Colors.black,
inactiveColor: Colors.black,
),
BottomNavyBarItem(
icon: Icon(
Icons.open_in_new_outlined,
),
title: Text('Know analysis'),
activeColor: Colors.black,
inactiveColor: Colors.black,
),
],
),
);
}
}
I want a help on how to check each list individually and be able to use the data of the selected list.
this image display my problem
You can do it by storing the selected index in an array.
Here is the idea.
final selectedIndexes = [];
/// In List View
ListView.builder(
itemBuilder: (_, index) {
return Container(
child: Row(
children: [
SizedBox(width: 10),
Checkbox(
value: selectedIndexes.contains(index),
onChanged: (bool value) {
if(selectedIndexes.contains(index) {
selectedIndexes.remove(index); // unselect
} else {
selectedIndexes.add(index); // select
}
setState(() {});
},
checkColor: Colors.greenAccent,
activeColor: Colors.black,
),
Text(
"${sympotms[index]['SymptomsName']}",
style: TextStyle(fontSize: 17.0),
),
],
),
);
},
itemCount: sympotms.length,
)
Since now you have stored the indexes, you can get whatever data you want for selected checkboxes.

Flutter api reloads every navigation

Whenever I change the page and come back, the api is reloaded. I have tried many suggestions. I would be glad if you help.
Here are the methods I tried :
How to avoid reloading data every time navigating to page
How to parse JSON only once in Flutter
Flutter Switching to Tab Reloads Widgets and runs FutureBuilder
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'models/Word.dart';
class WordListPage extends StatefulWidget {
WordListPage(Key k) : super(key: k);
#override
_WordListPageState createState() => _WordListPageState();
}
class _WordListPageState extends State<WordListPage> {
Future<List<Word>> data;
bool isSearching = false;
TextEditingController myController = TextEditingController();
List<Word> _words = List<Word>();
List<Word> _wordsForDisplay = List<Word>();
var keyListPage = PageStorageKey('list_page_key');
Future<List<Word>> getWord() async {
var response = await http.get("myAPIurl");
var _words = List<Word>();
_words = (json.decode(utf8.decode(response.bodyBytes)) as List)
.map((singleWordMap) => Word.fromJsonMap(singleWordMap))
.toList();
return _words;
}
#override
void initState() {
getWord().then((value) {
setState(() {
_words.addAll(value);
_wordsForDisplay = _words;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: myFutureBuilder(),
appBar: AppBar(
leading: Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: 'Total',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 10,
color: Colors.white,
),
),
TextSpan(
text: '\n${_words.length.toString()}',
style: TextStyle(
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 12,
),
),
TextSpan(
text: '\nLetter',
style: TextStyle(
decoration: TextDecoration.none,
color: Colors.white,
fontSize: 10,
),
),
],
),
),
),
centerTitle: true,
title: !isSearching
? Text('My Title')
: TextField(
autofocus: true,
style: TextStyle(color: Colors.white),
controller: myController,
onChanged: (value) {
value = value.toLowerCase();
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(value);
},
).toList();
},
);
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordPronounce = word.pronunciation.toLowerCase();
return wordPronounce.contains(value);
},
).toList();
},
);
},
decoration: InputDecoration(
isCollapsed: true,
icon: Icon(
Icons.menu_book,
color: Colors.white,
),
hintText: 'Search',
hintStyle: TextStyle(color: Colors.white),
),
),
actions: [
isSearching
? IconButton(
icon: Icon(Icons.cancel_outlined),
onPressed: () {
setState(
() {
this.isSearching = false;
myController.clear();
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(wordTitle);
},
).toList();
},
);
},
)
: IconButton(
icon: Icon(Icons.search_sharp),
onPressed: () {
setState(
() {
this.isSearching = true;
},
);
},
),
],
),
);
}
FutureBuilder<List<Word>> myFutureBuilder() {
return FutureBuilder(
future: getWord(),
builder: (context, AsyncSnapshot<List<Word>> snapshot) {
if (snapshot.hasData) {
return myWordListView(snapshot);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
ListView myWordListView(AsyncSnapshot<List<Word>> snapshot) {
return ListView.builder(
itemCount: _wordsForDisplay.length,
itemBuilder: (context, index) {
return ExpansionTile(
title: Text(
_wordsForDisplay[index].word,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.0),
),
subtitle: Text(
snapshot.data[index].pronunciation[0].toUpperCase() +
snapshot.data[index].pronunciation.substring(1),
),
leading: CircleAvatar(
child: Text(snapshot.data[index].word[0]),
),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 7.0, horizontal: 19.0),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: snapshot.data[index].word + ' : ',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: snapshot.data[index].meaning),
],
),
),
),
),
],
),
],
);
},
);
}
}
When you navigate and return back, the build method is called. Here you have "myFutureBuilder" placed as the body widget of Scaffold, thus this code get executed, within it "getWord" method is called, an it fetches data from the api everytime.
I suggest you to remove "myFutureBuider" and use "myWirdListView" directly as the body of the scaffold. Change myWordListView(List<Word> listOfWord) to use the word list you have already fetched in the initState() .
You need to to separate your api call from your ui. That way your api will only get called when you want it to. I recommend using some kind of external state management library, such as Provider, BLoC, or RxDart. Flutter will rebuild a widget anytime it wants to, beyond when you trigger it.
you passed getWord() as a function to you future builder so each time the widget get rebuilt, it is called.
To solve that, declare a variable Future<Word> getWordFuture ; , Inside initState assign getWordFuture = getWord(); and use getWordFuture in the Future builder.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'models/Word.dart';
class WordListPage extends StatefulWidget {
WordListPage(Key k) : super(key: k);
#override
_WordListPageState createState() => _WordListPageState();
}
class _WordListPageState extends State<WordListPage> {
Future<List<Word>> data;
bool isSearching = false;
TextEditingController myController = TextEditingController();
List<Word> _words = List<Word>();
List<Word> _wordsForDisplay = List<Word>();
var keyListPage = PageStorageKey('list_page_key');
Future<List<Word>> getWordFuture = Future<List<Word>> ; //1
Future<List<Word>> getWord() async {
var response = await http.get("myAPIurl");
var _words = List<Word>();
_words = (json.decode(utf8.decode(response.bodyBytes)) as List)
.map((singleWordMap) => Word.fromJsonMap(singleWordMap))
.toList();
return _words;
}
#override
void initState() {
getWordFuture = getWord(); //2
getWord().then((value) {
setState(() {
_words.addAll(value);
_wordsForDisplay = _words;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: myFutureBuilder(),
appBar: AppBar(
leading: Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: 'Total',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 10,
color: Colors.white,
),
),
TextSpan(
text: '\n${_words.length.toString()}',
style: TextStyle(
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 12,
),
),
TextSpan(
text: '\nLetter',
style: TextStyle(
decoration: TextDecoration.none,
color: Colors.white,
fontSize: 10,
),
),
],
),
),
),
centerTitle: true,
title: !isSearching
? Text('My Title')
: TextField(
autofocus: true,
style: TextStyle(color: Colors.white),
controller: myController,
onChanged: (value) {
value = value.toLowerCase();
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(value);
},
).toList();
},
);
setState(
() {
_wordsForDisplay = _words.where(
(word) {
var wordPronounce = word.pronunciation.toLowerCase();
return wordPronounce.contains(value);
},
).toList();
},
);
},
decoration: InputDecoration(
isCollapsed: true,
icon: Icon(
Icons.menu_book,
color: Colors.white,
),
hintText: 'Search',
hintStyle: TextStyle(color: Colors.white),
),
),
actions: [
isSearching
? IconButton(
icon: Icon(Icons.cancel_outlined),
onPressed: () {
setState(
() {
this.isSearching = false;
myController.clear();
_wordsForDisplay = _words.where(
(word) {
var wordTitle = word.word.toLowerCase();
return wordTitle.contains(wordTitle);
},
).toList();
},
);
},
)
: IconButton(
icon: Icon(Icons.search_sharp),
onPressed: () {
setState(
() {
this.isSearching = true;
},
);
},
),
],
),
);
}
FutureBuilder<List<Word>> myFutureBuilder() {
return FutureBuilder(
//future: getWord(),
future:getWordFuture,
builder: (context, AsyncSnapshot<List<Word>> snapshot) {
if (snapshot.hasData) {
return myWordListView(snapshot);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
ListView myWordListView(AsyncSnapshot<List<Word>> snapshot) {
return ListView.builder(
itemCount: _wordsForDisplay.length,
itemBuilder: (context, index) {
return ExpansionTile(
title: Text(
_wordsForDisplay[index].word,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.0),
),
subtitle: Text(
snapshot.data[index].pronunciation[0].toUpperCase() +
snapshot.data[index].pronunciation.substring(1),
),
leading: CircleAvatar(
child: Text(snapshot.data[index].word[0]),
),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 7.0, horizontal: 19.0),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: snapshot.data[index].word + ' : ',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: snapshot.data[index].meaning),
],
),
),
),
),
],
),
],
);
},
);
}
}

My Content from API is not being shown in Flutter

I wrote my code in flutter, and used NewsAPI.org for getting content about news, such as heading, image, content etc. I made a class "News" and ArticleModel() for retrieving and using the information. I used Conditional Operator (? :) for checking if the data is received, then show it, else CircularProgressIndiactor() is shown. After running the app, CircularProgressIndiactor() shows up and no information is shown/loaded. Can anyone help me here??
No error or warning is shown, and code compiles successfully, but no information is shown up.
Here is the main file, home.dart -
import 'package:flutter/material.dart';
import 'package:news_app/helper/data.dart';
import 'package:news_app/helper/news.dart';
import 'package:news_app/models/article_model.dart';
import 'package:news_app/models/category_models.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<CategoryModel> categories = new List<CategoryModel>();
List<ArticleModel> articles = new List<ArticleModel>();
bool loading = true;
#override
void initState() {
// TODO: implement initState
super.initState();
categories = getCategories();
getNews();
}
getNews() async {
News newsClass = News();
await newsClass.getNews();
articles = newsClass.news;
setState(() {
loading = false;
print('Done');
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Flutter',
style: TextStyle(
color: Colors.black,
),
),
Text(
'News',
style: TextStyle(
color: Colors.blue,
),
),
],
),
//elevation: 2.0,
),
body: loading
? Center(
child: Container(
child: CircularProgressIndicator(),
),
)
: SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
///Categories
Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
height: 70.0,
child: ListView.builder(
itemCount: categories.length,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemBuilder: (context, index) {
return CategoryTile(
imageUrl: categories[index].imageUrl,
categoryName: categories[index].categoryName,
);
},
),
),
///Blogs
Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: articles.length,
itemBuilder: (context, index) {
return BlogTile(
imageUrl: articles[index].urlToImage,
title: articles[index].title,
desc: articles[index].description,
);
},
),
),
],
),
),
),
),
);
}
}
class CategoryTile extends StatelessWidget {
final imageUrl, categoryName;
CategoryTile({this.imageUrl, this.categoryName});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {},
child: Container(
margin: EdgeInsets.only(right: 16.0),
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(6.0),
child: Image.network(
imageUrl,
width: 120.0,
height: 160.0,
fit: BoxFit.cover,
),
),
Container(
alignment: Alignment.center,
width: 120.0,
height: 60.0,
decoration: BoxDecoration(
color: Colors.black26,
borderRadius: BorderRadius.circular(6.0)),
child: Text(
categoryName,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 14.0,
),
),
),
],
),
),
);
}
}
class BlogTile extends StatelessWidget {
final String imageUrl, title, desc;
BlogTile(
{#required this.imageUrl, #required this.desc, #required this.title});
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Image.network(imageUrl),
Text(title),
Text(desc),
],
),
);
}
}
Here is the News.dart file -
import 'dart:convert';
import 'package:news_app/models/article_model.dart';
import 'package:http/http.dart' as http;
class News {
List<ArticleModel> news = [];
Future<void> getNews() async{
String url="http://newsapi.org/v2/top-headlines?country=in&category=business&apiKey=xxxxxxxxxxxxxxxxxx";
var response = await http.get(url);
var jsonData = jsonDecode(response.body);
if(jsonData["status"] == "ok") {
jsonData["articles"].forEach((element){
if(element['urlToImage'] != null && element['description'] != null) {
ArticleModel articleModel = ArticleModel(
title: element['title'],
author: element['author'],
description: element['description'],
url: element['url'],
urlToImage: element['urlToImage'],
content: element['content'],
);
news.add(articleModel);
}
});
}
}
}
And at last, ArticleModel.dart -
class ArticleModel {
String author, title, description;
String url, urlToImage;
String content;
ArticleModel({this.title, this.description, this.author, this.content, this.url, this.urlToImage});
}
Updating your request URL from http to https will give you expected result.
Update URL to: "https://newsapi.org/v2/top-headlines?country=in&category=business&apiKey="
Note: Don't share your API key in any open platforms for security reasons.

flutter_staggered_grid_view infinite scroll from api feed

I am using the flutter_staggered_grid_view package (https://github.com/letsar/flutter_staggered_grid_view) to have a feed of images. I want to implement something where first of all I can fetch say 30 images on initial screen load then when I reach the end and the scroll listener fires fetch more from the api. How do I change the below to do this? Can anyone give me any direction on docs etc. I think fundamentally this package is using gridview underneath but I am new to flutter so I'm unsure.
I just want an infinite scroll feed of images from an api.
class HomeScreen extends StatefulWidget {
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomeScreen> {
final scrollController = ScrollController();
#override
void initState() {
super.initState();
scrollController.addListener(() {
if (scrollController.position.maxScrollExtent ==
scrollController.offset) {
// Get more
print('End of screen');
}
});
}
List<String> imageList = [
'https://cdn.pixabay.com/photo/2020/12/15/16/25/clock-5834193__340.jpg',
'https://cdn.pixabay.com/photo/2020/09/18/19/31/laptop-5582775_960_720.jpg',
'https://media.istockphoto.com/photos/woman-kayaking-in-fjord-in-norway-picture-id1059380230?b=1&k=6&m=1059380230&s=170667a&w=0&h=kA_A_XrhZJjw2bo5jIJ7089-VktFK0h0I4OWDqaac0c=',
'https://cdn.pixabay.com/photo/2019/11/05/00/53/cellular-4602489_960_720.jpg',
'https://cdn.pixabay.com/photo/2017/02/12/10/29/christmas-2059698_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/01/29/17/09/snowboard-4803050_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/02/06/20/01/university-library-4825366_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/11/22/17/28/cat-5767334_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/12/13/16/22/snow-5828736_960_720.jpg',
'https://cdn.pixabay.com/photo/2020/12/15/16/25/clock-5834193__340.jpg',
'https://cdn.pixabay.com/photo/2020/09/18/19/31/laptop-5582775_960_720.jpg',
];
#override
Widget build(BuildContext context) {
/// If you set your home screen as first screen make sure call [SizeConfig().init(context)]
SizeConfig().init(context);
return Scaffold(
body: Platform.isIOS
? Container(
margin: EdgeInsets.only(left: 12, right: 12, top: 5),
child: CustomScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
slivers: <Widget>[
SliverAppBar(
floating: true,
title: SvgPicture.asset(
"assets/images/logo-dark.svg",
height: getProportionateScreenWidth(40),
),
actions: [
// Filter Button
FlatButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FilterScreen(),
),
),
child: Text(
"Filter",
style: Theme.of(context).textTheme.bodyText1,
),
),
],
),
CupertinoSliverRefreshControl(
onRefresh: () async {
await Future.delayed(Duration(seconds: 2));
},
),
SliverStaggeredGrid.countBuilder(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 12,
itemCount: imageList.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageList[index],
fit: BoxFit.cover,
),
),
);
},
staggeredTileBuilder: (index) {
return StaggeredTile.count(1, index.isEven ? 1.2 : 1.8);
},
)
],
),
)
: RefreshIndicator(
color: kMainColor,
displacement: 120,
onRefresh: () async {
await Future.delayed(Duration(seconds: 2));
},
child: Container(
margin: EdgeInsets.only(left: 12, right: 12, top: 5),
child: CustomScrollView(
controller: scrollController,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
slivers: <Widget>[
SliverAppBar(
floating: true,
title: SvgPicture.asset(
"assets/images/logo-dark.svg",
height: getProportionateScreenWidth(40),
),
actions: [
// Filter Button
FlatButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FilterScreen(),
),
),
child: Text(
"Filter",
style: Theme.of(context).textTheme.bodyText1,
),
),
],
),
SliverStaggeredGrid.countBuilder(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 12,
itemCount: imageList.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
child: ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(15),
),
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageList[index],
fit: BoxFit.cover,
),
),
);
},
staggeredTileBuilder: (index) {
return StaggeredTile.count(1, index.isEven ? 1.2 : 1.8);
},
)
],
),
),
),
);
}
}
You need to add the ScrollController for the scrolling detection at the bottom for the ListView and GridView. As you need the GridView i have created the ScrollController listner and added to the GridView's contollerfor the detection of the scroll. I have created the demo of it , please check it once. At first time it load the 10 items and when list comes to the bottom then it add more 10 items in it.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomeState();
}
}
class HomeState extends State<HomeScreen> {
List dataList = new List<int>();
bool isLoading = false;
int pageCount = 1;
ScrollController _scrollController;
#override
void initState() {
super.initState();
////LOADING FIRST DATA
addItemIntoLisT(1);
_scrollController = new ScrollController(initialScrollOffset: 5.0)
..addListener(_scrollListener);
}
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gridview',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.red,
accentColor: Color(0xFFFEF9EB),
),
home: Scaffold(
appBar: new AppBar(),
body: GridView.count(
controller: _scrollController,
scrollDirection: Axis.vertical,
crossAxisCount: 2,
mainAxisSpacing: 10.0,
physics: const AlwaysScrollableScrollPhysics(),
children: dataList.map((value) {
return Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 0.2,
margin: EdgeInsets.only(left: 10.0, right: 10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: Text("Item ${value}"),
);
}).toList(),
)));
}
//// ADDING THE SCROLL LISTINER
_scrollListener() {
if (_scrollController.offset >=
_scrollController.position.maxScrollExtent &&
!_scrollController.position.outOfRange) {
setState(() {
print("comes to bottom $isLoading");
isLoading = true;
if (isLoading) {
print("RUNNING LOAD MORE");
pageCount = pageCount + 1;
addItemIntoLisT(pageCount);
}
});
}
}
////ADDING DATA INTO ARRAYLIST
void addItemIntoLisT(var pageCount) {
for (int i = (pageCount * 10) - 10; i < pageCount * 10; i++) {
dataList.add(i);
isLoading = false;
}
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}
https://i.stack.imgur.com/7hcDc.gif

Flutter Error Valid value range is empty: 0

This error keeps coming up after my CircularProgressIndicator() is finished running for some time. I have no idea, why is this error coming and how can i fix it. I read the answers for the similar question asked before and tried them, but nothing changed. I am not sure that is this caused by my ListView.builder() or something else.
Here's the code -
main.dart -
import 'package:flutter/material.dart';
import 'package:soccer_app/api_manager.dart';
import 'package:soccer_app/pagerbody.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SoccerApp(),
);
}
}
class SoccerApp extends StatefulWidget {
#override
_SoccerAppState createState() => _SoccerAppState();
}
class _SoccerAppState extends State<SoccerApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFAFAFA),
appBar: AppBar(
backgroundColor: Color(0xFFFAFAFA),
elevation: 0.5,
title: Text('SoccerBoard', style: TextStyle(color: Colors.black),),
centerTitle: true,
),
body: FutureBuilder(
future: SoccerApi().getAllMatches(),
builder: (context, snapshot) {
if(snapshot.hasData) {
return PageBody(snapshot.data);
}
else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
Pagebody.dart -
import 'package:flutter/material.dart';
import 'package:soccer_app/goal_stat.dart';
import 'package:soccer_app/matchtile.dart';
import 'package:soccer_app/soccermodel.dart';
import 'package:soccer_app/teamstats.dart';
Widget PageBody(List<SoccerMatch> allmatches){
return Column(
children: [
Expanded(
flex: 2,
child: Container(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 18.0, vertical: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
teamStat("Local Team", allmatches[0].home.logoUrl, allmatches[0].home.name),
goalStat(allmatches[0].fixture.status.elapsedTime, allmatches[0].goal.home, allmatches[0].goal.away),
teamStat("Visitor Team", allmatches[0].away.logoUrl, allmatches[0].away.name),
],
),
),
),
),
Expanded(
flex: 5,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Color(0xff4373d9),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40.0),
topRight: Radius.circular(40.0),
),
),
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"MATCHES",
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
),
),
Expanded(
child: ListView.builder(
itemCount: allmatches.length,
itemBuilder: (context, index){
return MatchTile(allmatches[index]);
},
),
),
],
),
),
),
),
],
);
}
And this is the error -
The following RangeError was thrown building FutureBuilder<List<SoccerMatch>>(dirty, state: _FutureBuilderState<List<SoccerMatch>>#2a0fc):
RangeError (index): Invalid value: Valid value range is empty: 0
The relevant error-causing widget was:
FutureBuilder<List<SoccerMatch>> file:///C:/Users/Hp/AndroidStudioProjects/soccer_app/lib/main.dart:36:13
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:177:60)
#1 PageBody (package:soccer_app/pagerbody.dart:19:50)
#2 _SoccerAppState.build.<anonymous closure> (package:soccer_app/main.dart:40:20)
#3 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:751:55)
#4 StatefulElement.build (package:flutter/src/widgets/framework.dart:4744:28)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (10805): Api service: {get: fixtures, parameters: [], errors: {required: At least one parameter is required.}, results: 0, paging: {current: 1, total: 1}, response: []}
the list coming from your API is empty thus
List<SoccerMatch> allmatches is empty
and you are trying to call allmatches[0] (trying to access the first element of an empty list) so here is your error