Flutter - NetworkImage not changing or the selected gridview - flutter

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,
);
},
);
}
),
)
],
),
),
);
}
}

Related

Variable value (indicator) referenced among different widgets

I have a call that call this carousel and based on the widget clicked, it shows different content (through the contentUrls argument). The content shows up fine but I tried including the DotsIndicator widget and the position (activePage) variable is not updating. It takes its value from the ref. For instance, if on a widget I moved to image 5, when I go to a different carousel, it starts on image 5 rather than on image 0. I am not understanding how the activePage variable works through the setState.
import 'package:activo/widgets/video_player.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:dots_indicator/dots_indicator.dart';
class Carousel extends StatefulWidget {
const Carousel({super.key, required this.contentUrls});
final String contentUrls;
#override
State<Carousel> createState() => _CarouselState();
}
class _CarouselState extends State<Carousel> {
double activePage = 0.0;
final GlobalKey _key = GlobalKey();
#override
Widget build(BuildContext context) {
List<String> contentUrls = widget.contentUrls.split(' , ');
return Column(
children: [
CarouselSlider(
key: _key,
options: CarouselOptions(
height: 300.0,
onPageChanged: (val, _) {
setState(() {
activePage = val * 1.0;
});
},
),
items: contentUrls.map(
(currentContent) {
return Builder(
builder: (BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: Colors.white,
),
),
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: currentContent.contains('mp4')
? VideoPlayerWidget(
videoUrl: currentContent,
)
: Image.network(
currentContent,
// will need to change it based on pictures for events
fit: BoxFit.fill,
),
),
);
},
);
},
).toList(),
),
Text('$activePage'),
DotsIndicator(
dotsCount: contentUrls.length,
position: activePage,
),
],
);
}
}
Thanks!
I am expecting that when I load this widget, the activePage's value is 0 or at least the last value for that specific carousel (as I have multiple) rather than the last values from some other widget.
Here is your basic code adjusted to work with an index using setState((){}).
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
class Carousel extends StatefulWidget {
const Carousel({super.key, required this.contentUrls});
final List<String> contentUrls;
#override
State<Carousel> createState() => _CarouselState();
}
class _CarouselState extends State<Carousel> {
int activePage = 1;
final GlobalKey _key = GlobalKey();
_onPageChanged(int index) {
activePage = index;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
CarouselSlider(
key: _key,
options: CarouselOptions(
height: 300.0,
onPageChanged: (index, _) {
_onPageChanged(index);
},
),
items: widget.contentUrls
.map((e) => Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.blue,
child: Center(child: Text(e)),
),
))
.toList()),
],
);
}
}
Also, as you mentioned at the end, you may want the last carousel card to show up when you load the page again. For that you can save the page index using a package to manage the app state like Provider.
Here is the example using Provider (also with GIF images fetched from online).
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CarouselNotifier()),
],
child: const MaterialApp(
home:
Material(child: Carousel(contentUrls: ['https://media.giphy.com/media/wW95fEq09hOI8/giphy.gif',
'https://media.giphy.com/media/SggILpMXO7Xt6/giphy.gif','https://media.giphy.com/media/KHJw9NRFDMom487qyo/giphy.gif'
])),
),
),
);
}
class Carousel extends StatelessWidget {
const Carousel({super.key, required this.contentUrls});
final List<String> contentUrls;
#override
Widget build(BuildContext context) {
return Consumer(
builder: (_, notifier, __) =>
CarouselSlider(
key: key,
options: CarouselOptions(
height: 300.0,
onPageChanged: (index, _) {
Provider.of<CarouselNotifier>(context, listen: false)
.setIndex(index);
},
),
items: contentUrls
.map((e) =>
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.blue,
// display images from urls etc
child: Column(
children: [
Expanded(
flex: 3,
child: Center(child: Image.network(e))),
Expanded(child: Text('Page index ${contentUrls.indexOf(e)}'))
],
),
),
))
.toList()),
);
}
}
class CarouselNotifier extends ChangeNotifier {
int activePage = 0;
setIndex(int index) {
activePage = index;
notifyListeners();
}
}

Random sources for infinite scrolling GridView items

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(),
);
}
},
);
}
}

FutureBuilder operated without initState in flutter

I work with FutureBuilder to view a set of data through GridView by FutureBuilder but there are one problem the data is view without put method in initState().I don't know why it works without putting it in initState().
full code:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
bool showicon = false;
var apiURL;
Future getdataCat() async {
setState(() {
showicon = true;
});
apiURL = '***********************';
var response = await http.post(Uri.parse(apiURL));
var responsebody = jsonDecode(response.body);
if (responsebody.length > 0) {
return responsebody;
} else {
showicon = false;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Flexible(
child: FutureBuilder(
future: getdataCat(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
// still waiting for data to come
return showicon
? Center(
child: CircularProgressIndicator(
color: Colors.black,
))
: SizedBox(
height: 10,
child: Center(
child: Image.asset(
'assets/data.png',
)));
} else if (snapshot.hasData &&
snapshot.data.isEmpty &&
snapshot.data <= 0) {
return SizedBox(
height: 10,
child: Center(
child: Image.asset(
'assets/data.png',
)));
} else {
return GridView.builder(
physics: ScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, childAspectRatio: 3.4),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Container(
child: Card(
child: Column(
children: <Widget>[
Flexible(
child: GestureDetector(
child: Column(children: <Widget>[
Center(
child: Text(
"${snapshot.data[index]['name']}"))
]),
)),
],
),
),
);
},
);
}
},
),
)
],
),
),
);
}
}
As you can see I not make any thing in initState() but it's still working.
I need to stop it if I don't put it in initState().Because I need to run a different function before it.
I prefer this way. You can check Randal L. Schwartz video
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final Future<List<int>?> myFuture;
#override
void initState() {
super.initState();
myFuture = getCatData();
}
Future<List<int>?> getCatData() async {
await Future.delayed(Duration(seconds: 2));
//your operations
return [1, 2, 5];
// return [];
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
setState(() {});
}),
body: Center(
child: Column(
children: <Widget>[
Flexible(
child: FutureBuilder<List<int>?>(
future: myFuture,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text("Error ${snapshot.error}");
}
if (!snapshot.hasData) {
return Text("no Data found");
}
if (snapshot.data!.isEmpty) {
return Text("Empty found");
}
if (snapshot.hasData) {
return GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3.4,
),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Container(
child: Text(snapshot.data[index].toString()));
},
);
}
return Text("NA");
},
),
)
],
),
),
);
}
}
Point of FutureBuilder is to run a function that will return with the data you want to use to build your widgets.
You already call your method in the FutureBuilder:
FutureBuilder(
future: getdataCat(),
builder: (context, AsyncSnapshot snapshot) {...}
)

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

Set state from FutureBuilder in Flutter

I've been trying to set a property based on a response from a FutureBuilder but can't seem to get it working. How can I set _activityLength after the future resolves without setting during build?
FutureBuilder<QuerySnapshot>(
future: _future,
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start');
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
final documents = snapshot.data.documents;
_activityLength = documents.length;
return Expanded(
child: ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 0,
),
itemCount: documents.length,
itemBuilder: (context, index) => _activityTile(
documents[index],
),
),
);
}
}
},
)
The FutureBuilde is in a Column widget in the body of the Scaffold and the value that I need to set is in the _itemsHeaderText Something like this:
body:
...
child: Column(
children: <Widget>[
Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey, width: 1.0),
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 15.0,
right: 15.0,
top: 10.0,
bottom: 10.0),
child: _itemsHeaderText(),
),
),
_itemsBody(),
],
),
You can copy paste run full demo code below
You can use _future.then((value) in initState() and do job in addPostFrameCallback like setState
And after future resolves, you can get value of _future, you can do further processing if need
In demo code, I get value length and display on screen
You can see working demo, data length change from 0 to 9
code snippet
#override
void initState() {
super.initState();
_future = _getUsers();
_future.then((value) {
print("data length ${value.length}");
WidgetsBinding.instance.addPostFrameCallback((_) {
print("other job");
setState(() {
_dataLength = value.length;
});
});
});
}
working demo
output
I/flutter (18414): data length 9
I/flutter (18414): other job
full code
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: CategoryTab(title: 'Flutter Demo Home Page'),
);
}
}
class CategoryTab extends StatefulWidget {
CategoryTab({Key key, this.title}) : super(key: key);
final String title;
#override
_CategoryTabState createState() => _CategoryTabState();
}
class _CategoryTabState extends State<CategoryTab> {
Future<List<CategoryList>> _future;
int _dataLength = 0;
Future<List<CategoryList>> _getUsers() async {
var data = await http
.get("https://appiconmakers.com/demoMusicPlayer/API/getallcategories");
var jsonData = json.decode(data.body);
List<CategoryList> cat = [];
for (var u in jsonData) {
CategoryList categoryList = CategoryList(
u["category_id"],
u["category_name"],
u["parent_category_id"],
u["category_status"],
u["created_date"]);
cat.add(categoryList);
}
return cat;
}
#override
void initState() {
super.initState();
_future = _getUsers();
_future.then((value) {
print("data length ${value.length}");
WidgetsBinding.instance.addPostFrameCallback((_) {
print("other job");
setState(() {
_dataLength = value.length;
});
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Categories",
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
body: Column(
children: [
Text("data length $_dataLength"),
Expanded(
child: Container(
child: FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(child: Center(child: Text("Loading...")));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(),
title: Text(
"${snapshot.data[index].categoryName}",
// subtitle: Text(snapshot.data[index].categoryId
),
);
},
);
}
},
),
),
),
],
),
);
}
}
class CategoryList {
String categoryId;
String categoryName;
String parentCategoryId;
String categoryStatus;
String createdDate;
CategoryList(this.categoryId, this.categoryName, this.parentCategoryId,
this.categoryStatus, this.createdDate);
}
There are a few workarounds to your question.
You could hoist the futureBuilder up the widget tree, instead of setting state the state will be reset once ConnectionState.done.
You could place the future in a function that is called on init state, then the result of the future you set state on it.