How to scroll two SingleChildScrollView same time in Flutter? - flutter

I want to scroll two SingleChildScrollView same time vertically.

This code works for both SingleChildScrollViews.
import 'package:flutter/material.dart';
class ScrollTestPage extends StatefulWidget {
const ScrollTestPage({Key? key}) : super(key: key);
#override
State<ScrollTestPage> createState() => _ScrollTestPageState();
}
class _ScrollTestPageState extends State<ScrollTestPage> {
final _controller1 = ScrollController();
final _controller2 = ScrollController();
#override
void initState() {
super.initState();
_controller1.addListener(listener1);
_controller2.addListener(listener2);
}
var _flag1 = false;
var _flag2 = false;
void listener1() {
if (_flag2) return;
_flag1 = true;
_controller2.jumpTo(_controller1.offset);
_flag1 = false;
}
void listener2() {
if (_flag1) return;
_flag2 = true;
_controller1.jumpTo(_controller2.offset);
_flag2 = false;
}
#override
void dispose() {
super.dispose();
_controller1.removeListener(listener1);
_controller2.removeListener(listener2);
_controller1.dispose();
_controller2.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: ListView.builder(
controller: _controller1,
itemBuilder: (context, i) {
return Container(
margin: const EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Text('First List View $i'),
);
},
itemCount: 100,
),
),
Expanded(
child: ListView.builder(
controller: _controller2,
itemBuilder: (context, i) {
return Container(
margin: const EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Text('Second List View $i'),
);
},
itemCount: 100,
),
),
],
),
);
}
}

You can set the controller: parameter of both SingleChildScrollView widgets to the same controller. The controller has to be initialized like this:
final ScrollController _scrollController = ScrollController();

Related

I want to scroll up after the all childs of ListView have been scrolled up, parent widget of listview is SingleChildScrollView in flutter

I have SingleChildScrollView as a parent and in that, I have two listviews each list view is wrapped with SizedBox with a specific height (like 700), what I want is, when I scroll up all the views that are in the first list, the first Listview should scroll up and then I'll be able to scroll next Listview, Please have a look into the code below.
Your help means a lot to me.
Thank you in advance.
Note: I'm getting this required behavior in chrome but not on a mobile device
SingleChildScrollView( child: Column(children: [
SizedBox(
height: 700,
child:ListView.builder(
itemCount:
20, itemBuilder: (context, index) {
return const ListTile(leading: Icon(Icons.icecream,
color: Colors.amber,), title: Text("Ice Cream"),);
},),
),
SizedBox(
height: 300,
child: ListView.builder(
itemCount: 20, itemBuilder: (context, index) {
return const ListTile(
leading: Icon(Icons.cake, color: Colors.red,),
title: Text("Cake"),);
},),
),
],),)
You Can Do something like this on the Controllers:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ScrollingBehaviourInDart extends StatefulWidget {
const ScrollingBehaviourInDart({Key? key}) : super(key: key);
#override
State<ScrollingBehaviourInDart> createState() =>
_ScrollingBehaviourInDartState();
}
class _ScrollingBehaviourInDartState extends State<ScrollingBehaviourInDart> {
late ScrollController _sc1;
late ScrollController _sc2;
late ScrollController _sc3;
#override
void initState() {
_sc1 = ScrollController();
_sc2 = ScrollController();
_sc3 = ScrollController();
var _pr = Provider.of<MyScrollProvider>(context, listen: false);
_sc1.addListener(() {
log("SC1::::::::::: " + _sc1.position.pixels.toString());
if (_sc1.position.pixels == _sc1.position.minScrollExtent) {
print("OK");
_pr.changePhysics(enableScrolling: true);
}
});
_sc2.addListener(() {
if (_sc2.offset == _sc2.position.maxScrollExtent) {
_pr.changePhysics(enableScrolling: false);
log("YAAA");
}
});
_sc3.addListener(() {
log("SC3::::::::::: " + _sc3.position.pixels.toString());
});
super.initState();
}
#override
void dispose() {
_sc1.dispose();
_sc2.dispose();
_sc3.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var _size = MediaQuery.of(context).size;
return SafeArea(
child: Scaffold(
backgroundColor: Colors.white.withOpacity(0.8),
body: SizedBox(
height: _size.height,
child: Consumer<MyScrollProvider>(
builder: (context, myScrollProvider, _) => SingleChildScrollView(
controller: _sc1,
child: Column(
children: [
SizedBox(
height: _size.height * 0.5,
child: ListView.builder(
controller: _sc2,
physics: myScrollProvider.enablePrimaryScroll
? const AlwaysScrollableScrollPhysics()
: const NeverScrollableScrollPhysics(),
itemCount: 20,
shrinkWrap: true,
itemBuilder: (context, index) {
return const ListTile(
leading: Icon(
Icons.icecream,
color: Colors.amber,
),
title: Text("Ice Cream"),
);
},
),
),
ListView.builder(
itemCount: 20,
controller: _sc3,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const ListTile(
leading: Icon(
Icons.cake,
color: Colors.red,
),
title: Text("Cake"),
);
},
),
],
),
),
),
),
),
);
}
}
class MyScrollProvider extends ChangeNotifier {
var enablePrimaryScroll = true;
changePhysics({required bool enableScrolling}) {
enablePrimaryScroll = enableScrolling;
notifyListeners();
}
}
maybe you can use Stickyheader.
import 'package:sticky_headers/sticky_headers.dart';
ListView(
shrinkwarp:true,
children:[
StickyHeader(
head: Text('List 1 '),
content : ListView.builder(
physics: const ClampingScrollPhysics(), // use this for clamping scroll
itemBuilder: (context, idx) => Container(),
itemCount:5,
)
StickyHeader(
head: Text('List 2 '),
content : ListView.builder(
physics: const ClampingScrollPhysics(), // use this for clamping scroll
itemBuilder: (context, idx) => Container(),
itemCount:5,
)
]
}
There are many easy ways to handle this situation as stated by many other developers. I have created an Example class with ScrollController and AbsordPointer classes to achieve the required behavior.
Sample
class Example extends StatefulWidget {
const Example({super.key});
#override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
late ScrollController scrollController;
var reachedAtEnd = false;
#override
void initState() {
super.initState();
scrollController =ScrollController()..addListener(() {
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
reachedAtEnd = true;
setState(() {
});
}
},);
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: Column(
children: [
SizedBox(
height: 700,
child: ListView.builder(
controller: scrollController,
itemCount: 20,
itemBuilder: (context, index) {
return const ListTile(
leading: Icon(
Icons.icecream,
color: Colors.amber,
),
title: Text("Ice Cream"),
);
},
),
),
SizedBox(
height: 300,
child: AbsorbPointer(
absorbing: !reachedAtEnd,
child: ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return const ListTile(
leading: Icon(
Icons.cake,
color: Colors.red,
),
title: Text("Cake"),
);
},
),
),
),
],
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
// this variable determnines whether the back-to-top button is shown or not
bool _showBackToTopButton = false;
// scroll controller
late ScrollController _scrollController;
#override
void initState() {
_scrollController = ScrollController()
..addListener(() {
setState(() {
print(_scrollController.offset);
if (_scrollController.offset >= 400) {
_showBackToTopButton = true;
// _scrollToTop();
// show the back-to-top button
} else {
_showBackToTopButton = false; // hide the back-to-top button
}
});
});
super.initState();
}
#override
void dispose() {
_scrollController.dispose(); // dispose the controller
super.dispose();
}
// This function is triggered when the user presses the back-to-top button
void _scrollToTop() {
_scrollController.animateTo(0,
duration: const Duration(seconds: 3), curve: Curves.linear);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('com'),
),
body: ListView.builder(
controller: _scrollController,
itemCount: 40,
itemBuilder: (context, index) {
print(index);
if (index == 39) {
_scrollToTop();
}
return const ListTile(
leading: Icon(
Icons.icecream,
color: Colors.amber,
),
title: Text(' Ice Cream'),
);
},
),
);
}
}

Pagination scroll top in flutter

I'm currently create chat in flutter and get the last messages , I want to handle when scrolling to top to load more messages how can I create that ?
If you want to implement swipe to refresh kind of behaviour, you can use RefreshIndicator. See the example and usage in this YouTube video.
All you have to do is wrap your scrollable widget (it can be ListView or SingleChildScrollView) in a RefreshIndicator and provide onRefresh method:
class PullToRefresh extends StatelessWidget {
const PullToRefresh({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _refreshData,
child: ListView.builder( // or SingleChildScrollView
...
),
);
}
Future<void> _refreshData() async {
// load more items
}
}
ListView reverse: true displays the List from the bottom to the top.
and this is how to implement pagination
class HomeState extends State<Home> {
ScrollController? controller;
final _all = <WordPair>[];
final _saved = Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18.0);
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
bool isLoading = false;
#override
void initState() {
super.initState();
_all.addAll(generateWordPairs().take(20));
controller = ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
super.dispose();
controller?.dispose();
}
void _scrollListener() {
if (controller!.position.pixels == controller!.position.maxScrollExtent) {
startLoader();
}
}
void startLoader() {
setState(() {
isLoading = !isLoading;
fetchData();
});
}
fetchData() async {
var _duration = const Duration(seconds: 2);
return Timer(_duration, onResponse);
}
void onResponse() {
setState(() {
isLoading = !isLoading;
_all.addAll(generateWordPairs().take(20));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: const Text(
"List load more example",
style: TextStyle(color: Colors.white),
),
),
body: Stack(
children: <Widget>[
_buildSuggestions(),
_loader(),
],
),
);
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return Column(
children: <Widget>[
ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.check : Icons.check,
color: alreadySaved ? Colors.deepOrange : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},
),
const Divider(),
],
);
}
Widget _buildSuggestions() {
return ListView.builder(
reverse: true,
padding: const EdgeInsets.all(16.0),
controller: controller,
itemCount: _all.length,
itemBuilder: (context, i) {
return _buildRow(_all[i]);
});
}
Widget _loader() {
return isLoading
? const Align(
child: SizedBox(
width: 70.0,
height: 70.0,
child: Padding(
padding: EdgeInsets.all(5.0),
child: Center(child: CircularProgressIndicator())),
),
alignment: FractionalOffset.topCenter,
)
: const SizedBox(
width: 0.0,
height: 0.0,
);
}
}
You can get full code from Github HERE

StreamBuilder not updating widget

i have a Widget that has an AppBar with a progress bar, and a PageView with 4 pages, when moving between pages i am increasing / decreasing the progress bar.
I'm trying to do all the logic in my ViewModel.
This is my ViewModel (omitted non relevant stuff):
class RegisterViewModel extends BaseViewModel with RegisterViewModelInputs, RegisterViewModelOutputs {
final StreamController _sexStreamController = StreamController<int>.broadcast();
final StreamController _progressBarController = StreamController<double>.broadcast();
final StreamController _currentIndexController = StreamController<int>.broadcast();
final StreamController _isBackEnabled = StreamController<bool>.broadcast();
double _progress = 0.25;
int _index = 0;
#override
setCurrentIndex(int index) {
currentIndex.add(index);
}
#override
increaseProgress() {
if (_progress <= 1.0) {
_progress += 0.25;
progress.add(_progress);
}
}
#override
decreaseProgress() {
if (_progress > 0) {
_progress -= 0.25;
progress.add(_progress);
}
}
#override
setIsBackEnabled(int index) {
_isBackEnabled.add(index > 0 ? true : false);
}
#override
nextPage() {
if (_index < 4) {
_index++;
increaseProgress();
setCurrentIndex(_index);
}
}
#override
previousPage() {
if (_index > 0) {
_index--;
decreaseProgress();
setCurrentIndex(_index);
}
}
#override
Sink get currentIndex => _currentIndexController.sink;
#override
Sink get progress => _progressBarController.sink;
#override
Sink get isBackEnabled => _isBackEnabled.sink;
#override
Stream<int> get outputCurrentIndex => _currentIndexController.stream.map((currentIndex) => currentIndex);
#override
Stream<double> get outputProgress => _progressBarController.stream.map((progress) => progress);
#override
Stream<bool> get outputIsBackEnabled => outputIsBackEnabled.map((isEnabled) => isEnabled);
}
And here is my View:
class RegisterView extends StatefulWidget {
const RegisterView({Key? key}) : super(key: key);
#override
_RegisterViewState createState() => _RegisterViewState();
}
class _RegisterViewState extends State<RegisterView> {
final RegisterViewModel _viewModel = getIt<RegisterViewModel>();
final PageController _pageController = PageController(initialPage: 0);
final FixedExtentScrollController _weightScrollController = FixedExtentScrollController(initialItem: 80);
final FixedExtentScrollController _ageScrollController = FixedExtentScrollController(initialItem: 13);
final FixedExtentScrollController _heightScrollController = FixedExtentScrollController(initialItem: 13);
#override
void initState() {
_bind();
super.initState();
}
#override
void dispose() {
_viewModel.dispose();
super.dispose();
}
_bind() {
_viewModel.start();
}
#override
Widget build(BuildContext context) {
_viewModel.outputCurrentIndex.listen((index) {
_pageController.animateToPage(index, duration: const Duration(milliseconds: 1000), curve: Curves.ease);
});
List<Widget> pagesList = [
SexPage(
onConfirm: (sex) {
_viewModel.setSex(sex);
_viewModel.nextPage();
},
),
AgePage(
scrollController: _ageScrollController,
),
WeightPage(scrollController: _weightScrollController),
HeightPage(scrollController: _heightScrollController),
];
return Scaffold(
backgroundColor: ColorManager.backgroundColor,
appBar: AppBar(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: ColorManager.backgroundColor,
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.dark,
),
centerTitle: true,
title: AppBarWidget(_pageController),
elevation: AppSize.s0,
),
body: PageView(
reverse: true,
controller: _pageController,
physics: NeverScrollableScrollPhysics(),
children: [...pagesList],
),
);
}
}
class AppBarWidget extends StatelessWidget {
final PageController pageController;
final RegisterViewModel _viewModel = getIt<RegisterViewModel>();
AppBarWidget(
this.pageController, {
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
_viewModel.outputCurrentIndex.listen((index) {
pageController.animateToPage(index, duration: const Duration(milliseconds: 1000), curve: Curves.ease);
});
return Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
flex: 1,
child: InkWell(
child: Text(
AppStrings.skip,
style: Theme.of(context).textTheme.labelMedium,
),
onTap: () => _viewModel.nextPage(),
),
),
Expanded(
flex: 4,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: AppPadding.p60),
child: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(pi),
child: StreamBuilder<double>(
stream: _viewModel.outputProgress,
builder: (context, snapshot) {
return Progresso(
progress: snapshot.data ?? 0,
progressStrokeCap: StrokeCap.round,
backgroundStrokeCap: StrokeCap.round,
progressColor: ColorManager.primary,
backgroundColor: ColorManager.progressBarBackgroundGrey,
progressStrokeWidth: 10.0,
backgroundStrokeWidth: 10.0,
);
}),
),
),
),
StreamBuilder<int>(
stream: _viewModel.outputCurrentIndex,
builder: (context, snapshot) {
return Expanded(
flex: 1,
child: (snapshot.data ?? 0) > 0
? InkWell(
child: Row(
children: [
Text(
AppStrings.back,
style: Theme.of(context).textTheme.labelMedium,
),
Icon(
Icons.arrow_forward_ios,
color: ColorManager.subtitleGrey,
),
],
),
onTap: () => _viewModel.previousPage(),
)
: Container(),
);
}),
],
);
}
}
When i'm calling _viewModel.previousPage() & _viewModel.previousPage()` from the AppBarWidget, the progress bar view is updated, and there is a scroll animation to the next page.
But for some reason if the onConfirm callback:
onConfirm: (sex) {
_viewModel.setSex(sex);
_viewModel.nextPage();
}
is called from within SexPage, the scroll animation is working, but the progress bar view and the isBackEnabled is not updating.
I have checked and a new value is being added to the _progressBarController sink, but for some reason the StreamBuilder does not receive it? same for the isBackEnabled stream..
What am i doing wrong?
And another question i have is where should I listen to the outputCurrentIndex stream, and call _pageController.animateToPage()?
Apparently i had an issue with my Dependency Injection.
I'm using get_it and i used registerFactory, instead of registerLazySingleton.
Which probably made me have 2 separate ViewModels in each widget.

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.

Dynamic content PageView is not working in flutter

I tried to add dynamic content in pageview but the view of pageview is not showing.
class HomeFragment extends StatefulWidget {
#override
_HomeFragmentState createState() => _HomeFragmentState();
}
class _HomeFragmentState extends State<HomeFragment> {
List<String> pagerItems = new List();
#override
void initState() {
populatePager();
}
void populatePager() {
pagerItems.add(
"https://ecouponshop.com/wp-content/uploads/2016/04/20-848x470.jpg");
pagerItems.add(
"https://rukminim1.flixcart.com/flap/960/960/image/eb7785.jpg?q=50");
pagerItems.add(
"https://zamroo.s3.ap-south-1.amazonaws.com/images/product-images/home-garden/washing-machines/medium/20170803092151-23218.jpg");
setState(() {
pagerItems;
});
}
#override
Widget build(BuildContext context) {
return Container(
height: double.infinity,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
height: 200.0,
child: pager(),
)
],
),
),
);
}
Widget pager() {
return PageView.builder(
itemCount: pagerItems.length,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
pagerBody(context, index);
});
}
}
There are couple of mistakes in your code, like you forgot super.initState(), you were not returning any widget from itemBuilder. And you were unnecessarily using pager.items inside setState. Here is the correct code.
class HomeFragment extends StatefulWidget {
#override
_HomeFragmentState createState() => _HomeFragmentState();
}
class _HomeFragmentState extends State<HomeFragment> {
List<String> pagerItems = new List();
#override
void initState() {
super.initState(); // you forgot this
populatePager();
}
void populatePager() {
setState(() {
pagerItems.add("https://ecouponshop.com/wp-content/uploads/2016/04/20-848x470.jpg");
pagerItems.add("https://rukminim1.flixcart.com/flap/960/960/image/eb7785.jpg?q=50");
pagerItems.add("https://zamroo.s3.ap-south-1.amazonaws.com/images/product-images/home-garden/washing-machines/medium/20170803092151-23218.jpg");
});
}
#override
Widget build(BuildContext context) {
return Container(
height: double.infinity,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
height: 200.0,
child: pager(),
)
],
),
),
);
}
Widget pager() {
return PageView.builder(
itemCount: pagerItems.length,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return pagerBody(context, index); // you forgot this
},
);
}
}