Please find the code I have created. The problem here I face is the third item is getting called thrise when I increment the item count of the page view. Please let me know if there is any fix for this. I think this happens due to setState rebuild the widget. If I comment the setState it will work but dynamic page creation doesn't work. Let me know the fix for this.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../sub_screens/single_article_item.dart';
import '../sub_screens/two_article_item.dart';
import '../sub_screens/three_article_item.dart';
class ArticleScreen extends StatefulWidget {
#override
_ArticleScreenState createState() => _ArticleScreenState();
}
class _ArticleScreenState extends State<ArticleScreen> {
var homeDataList = List<Home>();
int _pageNo = 3;
void setPageNo(int page) {
setState(() {
_pageNo = page;
});
//notifyListeners();
}
#override
Widget build(BuildContext context) {
final vm = Provider.of<HomeViewModel>(context, listen: false);
homeDataList.add(Home("First Article Screen"));
homeDataList.add(Home("Second Article Screen"));
homeDataList.add(Home("Other type Article Screen"));
PageController controller = PageController(
keepPage: false,
initialPage: 0,
);
var currentPageValue = 0.0;
// return ChangeNotifierProvider(create: (ctx)=> LocationProvider(),child:MaterialApp(
// home: Scaffold(body: Container(child: LocationScreen(),)),
// ));
// }
// }
return MaterialApp(
home: SafeArea(
child: Scaffold(
body: Container(
child: PageView.builder(
controller: controller,
itemBuilder: (context, position) {
if (position == 0) {
//vm.fetchHomeData(":::third page");
print('first article');
return SingleArticleItem();
} else if (position == 1) {
print('two article');
return TwoArticleItem();
} else {
print("third article " +
_pageNo.toString() +
"::" +
position.toString());
return ThreeArticleItem(vm.getPageNo, vm.getHomeData);
}
},
onPageChanged: (index) {
if (index >= 1) {
// print('onPageChanged - two article');
controller.addListener(() {
setState(() {
currentPageValue = controller.page;
_pageNo = _pageNo + 1;
// print('onPageChanged - three article');
});
});
}
},
// itemCount: vm.getPageNo,
itemCount: vm.getPageNo,
scrollDirection: Axis.vertical,
),
),
),
),
);
}
}
Related
I am using a ScrollController in the ListView.builder widget.
But when I try to print something after reaching end of the screen it doesn't seem to work.
My main goal id to use it for pagination.
class RestaurantList extends StatefulWidget {
#override
_RestaurantListState createState() => _RestaurantListState();
}
class _RestaurantListState extends State<RestaurantList> {
ScrollController _controller;
void _scrollListener() {
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
print('new rests');
}
}
#override
void initState() {
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
#override
Widget build(BuildContext context) {
// print('restaurant list');
return FutureBuilder<List<Restaurant>>(
future: Provider.of<Restaurants>(context, listen: false)
.fetchAndSetRestaurants(),
builder: (context, snapshot) {
// Items are not available and you need to handle this situation, simple solution is to show a progress indicator
if (!snapshot.hasData) {
//Navigator.of(context).pushNamed('loading');
return Center(child: LoadingList());
}
final List<Restaurant> restaurants = snapshot.data;
print('List: ${restaurants[0].name}');
return ListView.builder(
controller: _controller,
physics: ScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (ctx, i) =>
restaurants[i].isAvailable || restaurants[i].totalQuantity != 0
? Column(
children: [
ChangeNotifierProvider.value(
value: restaurants[i], child: RestaurantInfo()),
Container(
height: 10,
)
],
)
: Container(),
itemCount: restaurants.length,
);
},
);
}
}
Try changing like below when checking reaching end of screen.
void _scrollListener() {
if (_controller.position.atEdge) {
if (_controller.position.pixels == 0) {
// Reaching Top
} else {
// Reaching end of screen
}
}
}
Hope Well,
I am using firebase realtime database on my flutter app (similar social media app). i have feed page and feed state page. i wanna show 10 posts first and after scroll bottom, load again 10 posts. i tried some methods but not working.
my codes
feed page code
Widget build(BuildContext context) {
var authstate = Provider.of<AuthState>(context, listen: false);
return Consumer<FeedState>(
builder: (context, state, child) {
final List<FeedModel> list = state.getPostList(authstate.userModel);
return CustomScrollView(
slivers: <Widget>[
child,
state.isBusy && list == null
? SliverToBoxAdapter(
child: Container(
height: fullHeight(context) - 135,
child: CustomScreenLoader(
height: double.infinity,
width: fullWidth(context),
backgroundColor: Colors.white,
),
),
)
: !state.isBusy && list == null
? SliverToBoxAdapter(
child: EmptyList(
'Follow someone',
subTitle:
'goto search page to find & follow Someone.\n When they added new post,\n they\'ll show up here.',
),
)
: SliverList(
delegate: SliverChildListDelegate(
list.map(
(model) {
return Container(
color: Colors.white,
child: Post(
model: model,
trailing: PostBottomSheet().PostOptionIcon(
context,
model: model,
type: PostType.Post,
scaffoldKey: scaffoldKey),
),
);
},
).toList(),
),
)
],
);
},
feed state code
List<FeedModel> get feedlist {
if (_feedlist == null) {
return null;
} else {
return List.from(_feedlist.reversed);
}
}
List<FeedModel> getPosttList(UserModel userModel) {
if (userModel == null) {
return null;
}
return feedlist;
}
I modified your code and use a ScrollController to load more data when the user reaches the end of the loaded data. (The data provider is hard-coded but you should be able to relate it to your scenario.) Note that I changed your code to use SliverChildBuilderDelegate which is more efficient.
import 'package:flutter/material.dart';
class ScrollTest extends StatefulWidget {
#override
_ScrollTestState createState() => _ScrollTestState();
}
class _ScrollTestState extends State<ScrollTest> {
bool isLoading = false;
bool isEnd = false;
final List<FeedModel> list = [];
ScrollController _controller;
_scrollListener() async {
var position = _controller.offset /
(_controller.position.maxScrollExtent -
_controller.position.minScrollExtent);
if (position > 0.5 && !_controller.position.outOfRange) {
await _getMoreData(list.length);
}
}
#override
void initState() {
super.initState();
_controller = ScrollController();
_controller.addListener(_scrollListener);
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
_getMoreData(list.length);
}
Future<void> _getMoreData(int index) async {
if (!isLoading) {
setState(() {
isLoading = true;
});
var tlist = await Feed.getPostList(index);
setState(() {
if (tlist.length == 0) {
isEnd = true;
} else {
list.addAll(tlist);
index = list.length;
}
isLoading = false;
});
}
}
#override
Widget build(BuildContext context) {
return CustomScrollView(
controller: _controller,
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: Colors.white,
height: 300,
child: Text(list[index].text),
);
},
childCount: list.length,
),
),
SliverFillRemaining(
child: Center(
child: isEnd ? Text('End') : CircularProgressIndicator(),
)),
],
);
}
}
// Dummy FeedModel
class FeedModel {
final String text;
FeedModel(this.text);
}
// Dummy Feed provider
class Feed {
static final data = [
FeedModel('1'),FeedModel('2'),FeedModel('3'),FeedModel('4'),
FeedModel('5'),FeedModel('6'),FeedModel('7'),FeedModel('8'),
FeedModel('9'),FeedModel('10'),FeedModel('11'),FeedModel('12'),
FeedModel('13'),
];
static Future<List<FeedModel>> getPostList(int index) async {
List<FeedModel> l = [];
for (var i = index; i < index + 5 && i < data.length; i++) {
l.add(data[i]);
}
await Future.delayed(Duration(seconds: 1));
return l;
}
}
Scroll automatically (without any user interaction) through all the ListTiles in the Listview using a Timer in flutter. The below method makes only one ListTile to animate but I want to animate all the ListTiles from top to bottom one by one and again from bottom to top one by one.
The below is the Listview:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder(
future: fetchNews(),
builder: (context, snap) {
if (snap.hasData) {
news = snap.data;
return ListView.separated(
//controller: _controller,
scrollDirection: scrollDirection,
controller: controller,
itemBuilder: (context, i) {
final NewsModel _item = news[i];
return AutoScrollTag(
key: ValueKey(i),
controller: controller,
index: i,
child: ListTile(
title: Text('${_item.title}'),
subtitle: Text(
'${_item.description}',
// maxLines: 1,
//overflow: TextOverflow.ellipsis,
),
),
);
},
separatorBuilder: (context, i) => Divider(),
itemCount: news.length,
);
} else if (snap.hasError) {
return Center(
child: Text(snap.error.toString()),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
}
}
This is the automatic scrolling i have tried:
#override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 2), (Timer t) async {
await controller.scrollToIndex(1,
preferPosition: AutoScrollPosition.begin);
});
Here is a solution assuming that all your items in the ListView have the same itemExtent.
In this solution, I highlight the current Item as selected. You could also want to stop autoscrolling as soon as you reach the bottom of the list.
Full source code
import 'dart:async';
import 'package:faker/faker.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part '66455867.auto_scroll.freezed.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
Future<List<News>> _fetchNews() async => dummyData;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('News')),
body: FutureBuilder(
future: _fetchNews(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return NewsList(newsList: snapshot.data);
} else if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
class NewsList extends StatefulWidget {
final List<News> newsList;
const NewsList({
Key key,
this.newsList,
}) : super(key: key);
#override
_NewsListState createState() => _NewsListState();
}
class _NewsListState extends State<NewsList> {
ScrollController _scrollController = ScrollController();
Timer _timer;
double _itemExtent = 100.0;
Duration _scrollDuration = Duration(milliseconds: 300);
Curve _scrollCurve = Curves.easeInOut;
int _autoScrollIncrement = 1;
int _currentScrollIndex = 0;
#override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(seconds: 2), (_) async {
_autoScrollIncrement = _currentScrollIndex == 0
? 1
: _currentScrollIndex == widget.newsList.length - 1
? -1
: _autoScrollIncrement;
_currentScrollIndex += _autoScrollIncrement;
_animateToIndex(_currentScrollIndex);
setState(() {});
});
}
void _animateToIndex(int index) {
_scrollController.animateTo(
index * _itemExtent,
duration: _scrollDuration,
curve: _scrollCurve,
);
}
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return ListView(
controller: _scrollController,
itemExtent: _itemExtent,
children: widget.newsList
.map((news) => ListTile(
title: Text(news.title),
subtitle: Text(
news.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
selected: widget.newsList[_currentScrollIndex].id == news.id,
selectedTileColor: Colors.amber.shade100,
))
.toList(),
);
}
}
#freezed
abstract class News with _$News {
const factory News({int id, String title, String description}) = _News;
}
final faker = Faker();
final dummyData = List.generate(
10,
(index) => News(
id: faker.randomGenerator.integer(99999999),
title: faker.sport.name(),
description: faker.lorem.sentence(),
),
);
Packages used in the solution:
freeze for the News Domain Class
build_runner to generate the freezed code
faker to generate the list of random news
UPDATE : Scroll only once
To stop the autoscrolling at the bottom of the listview, you just need to modify the initState method:
int _currentScrollIndex;
News _selectedNews;
#override
void initState() {
super.initState();
_currentScrollIndex = -1;
_timer = Timer.periodic(Duration(seconds: 2), (_) async {
setState(() {
if (_currentScrollIndex == widget.newsList.length - 1) {
_timer.cancel();
_selectedNews = null;
} else {
_selectedNews = widget.newsList[++_currentScrollIndex];
_animateToIndex(_currentScrollIndex);
}
});
});
}
We don't need the scroll direction defined as _autoScrollIncrement. However, I would introduce a new _selectedNews to easily unselect the last News item when we arrive at the bottom of the list. The selected flag of our ListTile would then become:
#override
Widget build(BuildContext context) {
return ListView(
[...]
children: widget.newsList
.map((news) => ListTile(
[...]
selected: _selectedNews?.id == news.id,
[...]
))
.toList(),
);
}
In the video and picture below, the horizontal and vertical widgets are arranged in order.
If you scroll through this, each widget will move separately, just like a video.
I want to make this move at once.
please enter the videoLink
https://firebasestorage.googleapis.com/v0/b/coody-f21eb.appspot.com/o/%E1%84%92%E1%85%AA%E1%84%86%E1%85%A7%E1%86%AB%20%E1%84%80%E1%85%B5%E1%84%85%E1%85%A9%E1%86%A8%202020-09-28%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%208.06.33.mov?alt=media&token=8a9d3fd0-1256-4d92-9a57-
please enter Imglink
https://firebasestorage.googleapis.com/v0/b/coody-f21eb.appspot.com/o/KakaoTalk_Photo_2020-09-28-08-15-13.jpeg?alt=media&token=77cd7fba-5b62-4d68-b760-8
import 'package:flutter/material.dart';
import 'element_homepage/contents_carousel.dart';
import 'element_homepage/gridView_of_homepage.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'product_detail.dart';
class HomeScreen extends StatefulWidget {
var stopTrigger = 1;
var unchanging ;
List<bool>bool_list_each_GridSell =[];
List<String> styleList = [];
var tf_copy = [];
final FirebaseUser user;
HomeScreen(this.user);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
super.initState();
if(widget.stopTrigger == 1){
setState(() {
widget.unchanging = Firestore.instance.collection("uploaded_product").snapshots();
});
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
appBar: AppBar(title:Text("logo --- rec --- menu")),
body: _bodyBuilder()
),
);
}
Widget _bodyBuilder() {
return Column(
children: [
ContentsCarousel(),
_gridBuilder()
],
);
}
Widget _gridBuilder() {
return Expanded(
child: StreamBuilder <QuerySnapshot>(
stream: _commentStream(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(!snapshot.hasData){
return Center(child: CircularProgressIndicator());
}
var items = snapshot.data?.documents ??[];
var fF = items.where((doc)=> doc['style'] == "오피스룩").toList();
var sF = items.where((doc)=> doc['style'] == "로맨틱").toList();
var tF = items.where((doc)=> doc['style'] == "캐주얼").toList();
fF.addAll(sF);
fF.addAll(tF);
widget.tf_copy.addAll(fF);
if(widget.stopTrigger == 2 ){
fF.shuffle();
widget.unchanging = fF;
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.6,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0,),
itemCount: fF.length,
itemBuilder: (BuildContext context, int index) {
for(var i=0; i<fF.length; i++){
widget.bool_list_each_GridSell.add(false);
}
return _buildListItem(context,widget.unchanging[index]);
}
);
},
),
);
}
Widget _buildListItem(context, document) {
return
InkWell(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context){
return ProductDetail(widget.user, document);
}));
},
child: Image.network(
document['thumbnail_img'],
fit : BoxFit.cover)
);
}
Stream<QuerySnapshot> _commentStream() {
widget.stopTrigger +=1;
if(widget.stopTrigger == 2 ){
return widget.unchanging;
}
}
}
I see you're attempting to achieve a behavior where a scroll on the GridView results in a scroll on the whole screen.
As the ContentsCarousel() and _gridBuilder() are in a Column, this behaviour cannot be achieved.
What I would suggest is wrapping your Column with a SingleChildScrollView widget.
I want to create a GridView that displays items which will be fetching from server by offset. I load only 10 items in GridView, and after user scroll reached to 10, I will load more 10 items. What are the best practices for implementing an infinite scroll GridView in Flutter?
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();
}
}
And output of the above program as follow
class AllOrdersPage extends StatefulWidget {
#override
_AllOrdersPageState createState() => _AllOrdersPageState();
}
class _AllOrdersPageState extends State<AllOrdersPage> {
List<OrderDatum> ordersList;
ScrollController _scrollController = ScrollController();
int skip = 0;
bool shouldLoadMore = true;
Future<OrdersResponse> future;
#override
void initState() {
super.initState();
ordersList = [];
future = getAllOrders(skip); //load data for first time
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) { //Check whether user scrolled to last position
if (shouldLoadMore) {
setState(() {
skip += ordersList.length;
future = getAllOrders(skip); //load more data
});
}
}
});
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<OrdersResponse>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasError)
return ErrorText('${snapshot.error.toString()}');
if (snapshot.hasData) {
skip = snapshot.data.skip;
if (snapshot.data.limit + snapshot.data.skip >=
snapshot.data.total) {
shouldLoadMore = false;
}
snapshot.data.data.forEach((element) {
if (!ordersList.contains(element)) ordersList.add(element);
});
if (skip == 0 && ordersList.isEmpty) {
return ErrorText('No orders.');
}
return Scrollbar(
controller: _scrollController,
isAlwaysShown: true,
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
childAspectRatio: 2.5,
mainAxisSpacing: 8),
controller: _scrollController,
padding: const EdgeInsets.all(8),
itemBuilder: (BuildContext context, int index) {
if (index == ordersList.length) {
return shouldLoadMore
? Center(child: CircularProgressIndicator())
: Container();
}
return Container(
width: MediaQuery.of(context).size.width,
child: OrderCard(ordersList[index]));
},
itemCount: ordersList.length + 1,
),
);
}
return Loader();
});
}
}
Thanks