Random sources for infinite scrolling GridView items - flutter

I want to have an infinite scrolling GridView page in which items have different sources, which is defined by randomly_select_URL function in my code. I need each item to have a different random_select_URL and selectedImage value while after running my code, all of the items are the same. Could anyone help with this?
The main page code
import 'package:flutter/material.dart';
import 'package:pet_store/utils/utils.dart';
import 'package:pet_store/widgets/random_pet_image.dart';
import 'webservice/API.dart';
import 'main.dart';
class Infinite_Scroll_Game extends StatefulWidget {
const Infinite_Scroll_Game({Key? key}) : super(key: key);
#override
State<Infinite_Scroll_Game> createState() => _Infinite_Scroll_GameState();
}
class _Infinite_Scroll_GameState extends State<Infinite_Scroll_Game> {
ScrollController _scrollController = ScrollController();
int pageNumber = 1;
var myRecipe;
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
pageNumber++;
setState(() {});
}
});
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: Colors.indigo,
title: const Text('Infinite Scroll Game'),
leading: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onTap: () {
// Navigator.pop(context);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => const HomePage(),
),
(route) => false,
);
},
),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 12.0),
child: FutureBuilder<List<dynamic>>(
future: API.get_pets(randomly_select_URL()),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic>? pet_data = snapshot.data;
var number_of_parameters = snapshot.data!.length;
var random_pet = random.nextInt(number_of_parameters);
return GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12.0,
mainAxisSpacing: 12.0,
),
itemBuilder: (BuildContext context, int index) {
return Random_Image_Card(
pet_data: pet_data, random_pet: random_pet);
},
);
} else if (snapshot.hasError) {
return Center(
child: Text('There was an error, Please try again'),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
}
}
The item code:
import 'package:double_back_to_close/toast.dart';
import 'package:flutter/material.dart';
import 'package:pet_store/utils/utils.dart';
class Random_Image_Card extends StatefulWidget {
List<dynamic>? pet_data;
int random_pet;
int current_index = 0;
Random_Image_Card(
{this.pet_data, required this.random_pet, Key? key})
: super(key: key);
#override
State<Random_Image_Card> createState() => _Random_Image_CardState();
}
class _Random_Image_CardState extends State<Random_Image_Card> {
List<dynamic> photoURL = [];
var number_of_photos;
var selectedImage;
#override
void initState() {
photoURL = widget.pet_data![widget.random_pet].photoUrls;
number_of_photos = photoURL.length;
selectedImage = random.nextInt(number_of_photos);
}
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
height: 180,
width: 180,
child: Card(
child: Container(
decoration: (photoURL.length != 0)
? BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: image(photoURL[selectedImage]).image,
fit: BoxFit.scaleDown),
)
: const BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: NetworkImage(
"https://cdn-cziplee-estore.azureedge.net//cache/no_image_uploaded-253x190.png"),
fit: BoxFit.scaleDown),
),
child: Text(""),
),
),
),
],
);
}
}

I added FutureBuilder to the item and just added the item in the main page and it fixed,
import 'package:double_back_to_close/toast.dart';
import 'package:flutter/material.dart';
import 'package:pet_store/utils/utils.dart';
import '../webservice/API.dart';
class Random_Image_Card extends StatefulWidget {
const Random_Image_Card({Key? key}) : super(key: key);
#override
State<Random_Image_Card> createState() => _Random_Image_CardState();
}
class _Random_Image_CardState extends State<Random_Image_Card> {
List<dynamic>? pet_data;
int random_pet = 0;
int current_index = 0;
List<dynamic> photoURL = [];
var number_of_photos;
var selectedImage;
var random_URL;
#override
Widget build(BuildContext context) {
random_URL = randomly_select_URL();
return FutureBuilder<List<dynamic>>(
future: API.get_pets(random_URL),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic>? pet_data = snapshot.data;
var number_of_parameters = snapshot.data!.length;
var random_pet = random.nextInt(number_of_parameters);
photoURL = pet_data![random_pet].photoUrls;
number_of_photos = photoURL.length;
selectedImage = random.nextInt(number_of_photos);
return Column(
children: [
SizedBox(
height: 180,
width: 180,
child: Card(
child: Container(
decoration: (photoURL.length != 0)
? BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: image(photoURL[selectedImage]).image,
fit: BoxFit.scaleDown),
)
: const BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: NetworkImage(
"https://cdn-cziplee-estore.azureedge.net//cache/no_image_uploaded-253x190.png"),
fit: BoxFit.scaleDown),
),
child: Text(""),
),
),
),
],
);
} else if (snapshot.hasError) {
return const Center(
child: Text('There was an error, Please try again'),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}

Related

Flutter - NetworkImage not changing or the selected gridview

I am trying to get images from firebase storage, then switch the image from the current to the image I selected. When I select an image, the color should turn from grey to green and switch the image using the index, however it doesn't replace the image nor the color when selected, I added a setState for the index and added a "?v=${Random().nextInt(100)}" to it so that it refreshes everytime something is selected, but still nothing happens, how can I fix that
code for the page
import 'dart:developer';
import 'dart:math';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gg_gg/cardGridView.dart';
class page3 extends StatefulWidget {
const page3({Key? key}) : super(key: key);
#override
State<page3> createState() => _page3State();
}
class _page3State extends State<page3> {
List<String> free_banners = ["freebanner1.jpg", "freebanner2.jpg", "freebanner3.jpg", "uae.jpg", "usa.jpg"];
Future<List<String>> getImages() async{
List<String> x = [];
for(var i = 0; i<free_banners.length; i++){
String url = await FirebaseStorage.instance.ref().child("calling_cards/").child(free_banners[i]).getDownloadURL();
x.add(url);
}
return x;
}
#override
Widget build(BuildContext context) {
int ind = 0;
void checkOption(int index, String url){
setState(() {
ind = index;
print(ind.toString());
});
}
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder(
future: getImages(),
builder: (BuildContext context, AsyncSnapshot snapshot){
return Container(
height: 300.h,
width: 150.w,
decoration: BoxDecoration(
color: Colors.red,
image: DecorationImage(
image: NetworkImage(snapshot.data[ind].toString() + "?v=${Random().nextInt(100)}")
)
),
);
}
),
SizedBox(width: 30.w,),
SizedBox(
height: 932.h,
width: 200.w,
child: FutureBuilder(
future: getImages(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return GridView.builder(
itemCount: 5,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
mainAxisSpacing: 10,
crossAxisSpacing: 5
),
itemBuilder: (BuildContext context, int index){
return cardGridView(
imageURL: snapshot.data[index].toString(),
onTap: () => checkOption(index, snapshot.data[index].toString()),
selected: ind == index,
);
},
);
}
),
)
],
),
),
);
}
}
code for cardGridView
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class cardGridView extends StatelessWidget {
const cardGridView({Key? key, this.imageURL, this.onTap, this.selected}) : super(key: key);
final String? imageURL;
final VoidCallback? onTap;
final bool? selected;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: Container(
height: 300.h,
width: 150.w,
color: selected?? false? Colors.green:Colors.grey,
child: Image.network(imageURL!),
),
),
);
}
}
Thanks.
UPDATE
turns out the problem was with the ind variable, since it's in the build widget, it will always stay 0, therefore if placed outside the build widget, then it can change.
here is the updated code:
import 'dart:developer';
import 'dart:math';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gg_gg/cardGridView.dart';
class page3 extends StatefulWidget {
const page3({Key? key}) : super(key: key);
#override
State<page3> createState() => _page3State();
}
class _page3State extends State<page3> {
List<String> free_banners = ["freebanner1.jpg", "freebanner2.jpg", "freebanner3.jpg", "uae.jpg", "usa.jpg"];
Future<List<String>> getImages() async{
List<String> x = [];
for(var i = 0; i<free_banners.length; i++){
String url = await FirebaseStorage.instance.ref().child("calling_cards/").child(free_banners[i]).getDownloadURL();
x.add(url);
}
return x;
}
int ind = 0;
#override
Widget build(BuildContext context) {
String card = "https://firebasestorage.googleapis.com/v0/b/ableflyerdatabase.appspot.com/o/calling_cards%2Ffreebanner1.jpg?alt=media&token=a0a6c01c-dc02-412c-8942-60ff25215c9d";
void checkOption(int index, String url){
setState(() {
ind = index;
card = url;
print(card);
print(ind.toString());
});
}
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder(
future: getImages(),
builder: (BuildContext context, AsyncSnapshot snapshot){
return Container(
height: 300.h,
width: 150.w,
decoration: BoxDecoration(
color: Colors.red,
image: DecorationImage(
image: NetworkImage(snapshot.data[ind].toString() + "?v=${Random().nextInt(100)}")
)
),
);
}
),
SizedBox(width: 30.w,),
SizedBox(
height: 932.h,
width: 200.w,
child: FutureBuilder(
future: getImages(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return GridView.builder(
itemCount: free_banners.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
mainAxisSpacing: 10,
crossAxisSpacing: 5
),
itemBuilder: (BuildContext context, int index){
return cardGridView(
imageURL: snapshot.data[index].toString(),
onTap: (){
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
ind = index;
card = snapshot.data[index].toString();
print(card);
print(ind.toString());
});
});
},
selected: ind == index,
);
},
);
}
),
)
],
),
),
);
}
}

How to find out where the click was in a dynamic list?

I have a list and I need to set the container's background when clicking on it. However, what I have now does not work. When clicked, the color of the entire list changes, not the selected one. It seems to me that I need to add an index somewhere. I can't put it in a separate widget, because I'm attached to the list. Tell me how to do it?
setState -
Color? _textColor;
Color? _bgColor;
void initState() {
_bgColor = configColors.orange;
_textColor = Colors.white;
super.initState();
}
List
ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: HomeStore.storage.length,
itemBuilder: (BuildContext ctx, index) {
return Row (
// mainAxisAlignment: MainAxisAlignment.start,
children: <Widget> [
InkWell(
onTap: () {
setState(() {
if (_bgColor ==
configColors
.orange) {
_bgColor =
Colors.white;
_textColor =
configColors
.textStorage;
} else {
_bgColor =
configColors.orange;
_textColor =
Colors.white;
}
}
);
},
child: Container(
width: 71.4,
height: 30.3,
decoration: BoxDecoration(
color: _bgColor,
borderRadius: BorderRadius.circular(10)
),
child: Align(
alignment: Alignment.center,
child: Text(HomeStore.storage[index], style: TextStyle(color: _textColor,),),
)
),
),
SizedBox(
width: 18,
),
],
);
}),
For single item selection, you can use a int variable, this snippet will help you to understand the concept.
int? selectedIndex;
onTap: () {
setState(() {
selectedIndex = index;
});
},
And to select color
color:selectedIndex == index ? Colors.red : Colors.blue
Test snippet
class Sg extends StatefulWidget {
Sg({Key? key}) : super(key: key);
#override
State<Sg> createState() => _SgState();
}
class _SgState extends State<Sg> {
int? selectedIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: 4,
itemBuilder: (BuildContext ctx, index) {
return Row(
// mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {
setState(() {
selectedIndex = index;
});
},
child: Container(
width: 71.4,
height: 30.3,
decoration: BoxDecoration(
color:
selectedIndex == index ? Colors.red : Colors.blue,
borderRadius: BorderRadius.circular(10)),
child: Align(
alignment: Alignment.center,
child: Text(
"HomeStore.storage[index]",
),
)),
),
],
);
}),
);
}
}
sharing one of my code demo
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget {
#override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
late int tappedIndex;
#override
void initState() {
super.initState();
tappedIndex = 0;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ListView.builder(
shrinkWrap: true,
itemCount: 4,
itemBuilder: (context, index) {
return Container(
color: tappedIndex == index ? Colors.blue : Colors.grey,
child: ListTile(
title: Center(
child: Text('${index + 1}'),
),onTap:(){
setState((){
tappedIndex=index;
});
}));
})
]));
}
}
taped index will solve problem

Update a variable after executing a future function

I have a function called ff which takes 2 images, and returns an outputimage , I"d like to update the original image, when the user taps on any of the style images, and display the outputimage instead of the original one . I know I should use setstate somehow, but I'm confused on how and where
code :
Future ff(String styleImagePath, String originalImagePath) async {
ImageTransferFacade showtime = ImageTransferFacade();
var original_image = showtime.loadoriginalImage(originalImagePath);
var style_image = showtime.loadStyleImage(styleImagePath);
var output_image = showtime.transfer(await original_image, await style_image);
return output_image;
}
class second extends StatefulWidget {
const second({
Key? key,
required this.image,
}) : super(key: key);
final XFile image;
#override
State<second> createState() => _secondState();
}
class _secondState extends State<second> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(16),
child: Card(
child: Image.file(File(widget.image
.path))))), // this is the original image which i'd like to replace once user taps on the style image
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: CarouselSlider.builder(
itemCount: imageList.length,
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
),
itemBuilder: (context, index, realIdx) {
return Container(
child: Center(
child: GestureDetector(
onTap: () => ff(imageList[index], widget.image.path),
child: Image.network(imageList[index],
fit: BoxFit.cover, width: 1000)),
));
},
),
),
floatingActionButton: const FloatingActionButton(onPressed: null),
);
}
}
Try this:
Future ff(String styleImagePath, String originalImagePath) async {
ImageTransferFacade showtime = ImageTransferFacade();
var original_image = showtime.loadoriginalImage(originalImagePath);
var style_image = showtime.loadStyleImage(styleImagePath);
var output_image = showtime.transfer(await original_image, await style_image);
return output_image;
}
class second extends StatefulWidget {
const second({
Key? key,
required this.image,
}) : super(key: key);
final XFile image;
#override
State<second> createState() => _secondState();
}
class _secondState extends State<second> {
late XFile _image;
initState(){
_image = widget.image;
super.initState();
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(16),
child: Card(
child: Image.file(File(image
.path))))), // this is the original image which i'd like to replace once user taps on the style image
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: CarouselSlider.builder(
itemCount: imageList.length,
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
),
itemBuilder: (context, index, realIdx) {
return Container(
child: Center(
child: GestureDetector(
onTap: () {
ff(imageList[index], widget.image.path).then((value){
setState((){
_image = value;
});
});
}, child: Image.network(imageList[index],
fit: BoxFit.cover, width: 1000)),
));
},
),
),
floatingActionButton: const FloatingActionButton(onPressed: null),
);
}
}

How to create load more listview in flutter

I want to create load more scrollview in listview. My app flow is storing youtube link in csv file and fetch this link from my app and display in my listview. But the problem is I don't want to wait too much load time when app is open.If I have a lot of youtube link in my csv.I will take a lot of time.So,for example I want to display only 5 video in initial state and when load more, display more 5 video in my list view.How can I do that.My code is below.
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'videolist.dart';
import './models/models.dart';
import 'package:csv/csv.dart' as csv;
import 'package:http/http.dart' as http;
class DisplayVideo extends StatefulWidget {
String id;
#override
DisplayVideo(this.id);
_DisplayVideoState createState() => _DisplayVideoState();
}
class _DisplayVideoState extends State<DisplayVideo> {
late YoutubePlayerController _controller ;
Future<List<YoutubeDetail>> _loadCSV() async {
Map<String, String> allData = {
'login': '',
'password': '',
};
final Uri url = Uri.parse(
'https://raw.githubusercontent.com/JornaldRem/bedtime_story/main/videoId.csv');
final response = await http.get(url);
csv.CsvToListConverter converter =
new csv.CsvToListConverter(eol: '\r\n', fieldDelimiter: ',');
List<List> listCreated = converter.convert(response.body);
// the csv file is converted to a 2-Dimensional list
List<YoutubeDetail> youtubeDetailList = [];
for (int i = 0; i < listCreated.length; i++) {
YoutubeDetail temp = YoutubeDetail(
listCreated[i][0],
listCreated[i][1],
);
youtubeDetailList.add(temp);
}
return youtubeDetailList;
}
#override
void initState() {
// TODO: implement initState
super.initState();
_controller = YoutubePlayerController(
initialVideoId: widget.id,
flags: YoutubePlayerFlags(
autoPlay: true,
mute: false,
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: Text('Title'),
toolbarHeight: 60,
backgroundColor: const Color(0xFF006666),
),
body: Column(
children: [
Container(
child: YoutubePlayer(
controller: _controller,
liveUIColor: Colors.amber,
),
),
Expanded(
child: Container(
child: FutureBuilder(
future: _loadCSV(),
builder: (BuildContext context,
AsyncSnapshot<List<YoutubeDetail>> snapshot) {
if (snapshot.hasData) {
List<YoutubeDetail> videoDetail = snapshot.data!;
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: videoDetail.length,
itemBuilder: (_, int index) {
if (index > 0) {
return GestureDetector(
child: Container(
height: 80,
child: DisplayVideoView(
videoDetail[index].url,
videoDetail[index].title),
),
onTap: (){
String url = videoDetail[index].url;
String id = url.substring(url.length - 11);
print("HEllo");
_controller.load(id);
// DisplayVideo(id);
}
);
} else {
return Container();
}
});
} else {
return Container();
}
}),
),
),
],
));
}
}
class DisplayVideoView extends StatelessWidget {
String videopath;
String title;
DisplayVideoView(this.videopath, this.title);
#override
Widget build(BuildContext context) {
String url = videopath;
String id = url.substring(url.length - 11);
// TODO: implement build
return Card(
clipBehavior: Clip.antiAlias,
child: Container(
height: 150,
padding: const EdgeInsets.all(0),
child: Row(children: [
Expanded(
flex: 6,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://img.youtube.com/vi/$id/mqdefault.jpg'),
fit: BoxFit.fill)),
),
),
Spacer(
flex: 1,
),
Expanded(
flex: 14,
child: Container(
padding: const EdgeInsets.only(top: 2),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold)),
],
),
),
),
]),
),
);
}
}
What do you think about this approach:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
List<Widget> _myList = [];
void _loadFiveMore() {
_myList = <Widget>[
..._myList,
for (int i = _myList.length; i < _myList.length + 5; i++)
ListTile(title: Text('item $i')),
];
}
#override
void initState() {
_loadFiveMore();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(children: [
..._myList,
OutlinedButton(
onPressed: () {
setState(() => _loadFiveMore());
},
child: const Text('get 5 more'))
]),
),
);
}
}
void main() {
runApp(const ExampleWidget());
}
You can use this package.
have loadmore callback, refresh call back
https://pub.dev/packages/loadmore_listview

How to add CircularProgressIndicator at the end of listview while waiting for request

I would like to have CircularProgressIndicator at the end of list if request for another portion of advertisements is being loaded. I guess it needs to be done under onNotification method, because there I make the request and maybe disable it when this method is done?
The code is similar to https://codinginfinite.com/flutter-future-builder-pagination/
Could you tell me how can I do it?
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:..../ui/pages/home/page/AdvertisementCard.dart';
import 'package:.../ui/pages/home/page/model/AdvertisementList.dart';
import '../../SizedBox.dart';
import 'AdvertisementProdRepository.dart';
import 'BottomAppBar.dart';
import 'FAB.dart';
import 'model/AdvertisementList.dart';
class HomePage extends StatefulWidget {
final String jwt;
const HomePage(this.jwt);
#override
_HomePage createState() => _HomePage();
factory HomePage.fromBase64(String jwt) => HomePage(jwt);
}
class _HomePage extends State<HomePage> {
late final String jwt;
late Future<AdvertisementList> _listOfItems;
final searchTextController = TextEditingController();
#override
void initState() {
super.initState();
jwt = widget.jwt;
_listOfItems = AdvertisementProdRepository.fetchAdvertisements(1);
}
#override
Widget build(BuildContext context) => Scaffold(
body: Scaffold(
backgroundColor: const Color(0xFEF9F9FC),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: buildFAB(),
bottomNavigationBar: BuildBottomAppBar(),
body: Container(
padding: EdgeInsets.only(left: 25.0, right: 25, top: 25),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: [
TextFormField(
controller: searchTextController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Szukaj',
fillColor: Color(0xffeeeeee),
filled: true),
),
buildSizedBox(20.0),
Padding(
padding: const EdgeInsets.only(left: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Najnowsze ogłoszenia',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
],
),
),
buildSizedBox(10.0),
FutureBuilder<AdvertisementList>(
future: _listOfItems,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return Expanded(
child:
AdvertisementTile(advertisements: snapshot.data!),
);
}
},
),
],
),
),
),
);
}
class AdvertisementTile extends StatefulWidget {
final AdvertisementList advertisements;
AdvertisementTile({Key? key, required this.advertisements}) : super(key: key);
#override
State<StatefulWidget> createState() => AdvertisementTileState();
}
class AdvertisementTileState extends State<AdvertisementTile> {
AdvertisementLoadMoreStatus loadMoreStatus =
AdvertisementLoadMoreStatus.STABLE;
final ScrollController scrollController = new ScrollController();
late List<Advertisement> advertisements;
late int currentPageNumber;
bool _loading = false;
#override
void initState() {
advertisements = widget.advertisements.items;
currentPageNumber = widget.advertisements.pageNumber;
super.initState();
}
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: onNotification,
child: Padding(
padding: const EdgeInsets.only(bottom: 28.0),
child: new ListView.separated(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
controller: scrollController,
itemCount: advertisements.length,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (_, index) {
return AdvertisementCard(data: advertisements[index]);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 10,
);
},
),
),
);
}
bool onNotification(ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
if (scrollController.position.maxScrollExtent > scrollController.offset &&
scrollController.position.maxScrollExtent - scrollController.offset <=
50) {
if (loadMoreStatus == AdvertisementLoadMoreStatus.STABLE) {
loadMoreStatus = AdvertisementLoadMoreStatus.LOADING;
AdvertisementProdRepository.fetchAdvertisements(currentPageNumber + 1)
.then((advertisementObject) {
currentPageNumber = advertisementObject.pageNumber;
loadMoreStatus = AdvertisementLoadMoreStatus.STABLE;
setState(() => advertisements.addAll(advertisementObject.items));
});
}
}
}
return true;
}
}
enum AdvertisementLoadMoreStatus { LOADING, STABLE }
You can take a variable and set it as false.
loadingNewData = false;
You can then wrap your listview with a column. After listview you can add the conditional code.
Column(children: [
Listview(),
if (loadingNewData) CircularProgressIndicator()
])
Now whenever you reach the end of listview, you can set the loadingNewData as true and after the data is loaded you can set it back to false.