I am currently making a calendar app that I would like to swipe right or left to go to the next or previous month. I am using a PageView by initially setting up an array with 3 items in it and the initial page being the second one. I would like to swipe right and add a page to the end. I would like to swipe left and add a page to the beginning. Currently, if you go to the right (adding pages to the end) it works great. But if you go to the left (adding pages to the beginning) there is some odd behavior and it doesn't work at all.
I have pasted a simple example below with counters. I am not sure if I am doing it right or if my logic is off. Can anyone let me know the correct way to do this?
import 'package:flutter/material.dart';
void main() => runApp(LimeApp());
class LimeApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Pageview Test',
home: MainPage(),
);
}
}
int _lowerCount = -1;
int _upperCount = 1;
class MainPage extends StatelessWidget {
final List<Widget> _pages = <Widget>[
new Center(child: new Text("-1", style: new TextStyle(fontSize: 60.0))),
new Center(child: new Text("0", style: new TextStyle(fontSize: 60.0))),
new Center(child: new Text("1", style: new TextStyle(fontSize: 60.0)))
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.symmetric(
vertical: 50.0,
),
child: PageView(
onPageChanged: (pageId){
if(pageId == _pages.length - 1){
print("Last page, add page to end");
_upperCount = _upperCount + 1;
_pages.add(new Center(child: new Text(_upperCount.toString(), style: new TextStyle(fontSize: 60.0))));
}
if(pageId == 0){
print("First page, add page to start");
_lowerCount = _lowerCount - 1;
_pages.insert(0, new Center(child: new Text(_lowerCount.toString(), style: new TextStyle(fontSize: 60.0))));
}
},
controller: PageController(
initialPage: 1,
),
children: _pages,
),
),
);
}
}
I think I got it working. I did not use the onPageChange
event. Instead I listened to changes in the controller.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const Widget _home = HomePage();
@override
Widget build(BuildContext context) => MaterialApp(
home: _home,
);
}
class HomePage extends StatefulWidget {
const HomePage();
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late PageController _controller;
// This points always to the mid-element in _list
late int _initialIndex;
// This should work with 3, 7, 11, ... odd elements. Mind the pattern!!!
List<int> _list = [-2, -1, 0, 1, 2];
@override
void initState() {
super.initState();
// Calculate mid.
_initialIndex = (_list.length / 2).floor();
_controller = PageController(initialPage: _initialIndex, viewportFraction: 0.8);
// This is where we listen to changes.
_controller.addListener(() {
// Get index according to the direction
// _controller.page! > _initialIndex => swiping to the right, going to the left / previous element
// _controller.page! < _initialIndex => swiping to the left, going to the right / next element
final index = _controller.page! > _initialIndex ? _controller.page!.floor() : _controller.page!.ceil();
if (index == _initialIndex) return;
if (index == _initialIndex - 1) {
_prev();
} else if (index == _initialIndex + 1) {
_next();
}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
// Update list and jump to the middle element
void _next() {
setState(() {
_list
..removeAt(0)
..insert(_list.length, _list.last + 1);
// Update current DateTime here
});
_controller.jumpToPage(_initialIndex);
}
// Update list and jump to the middle element
void _prev() {
setState(() {
_list
..insert(0, _list.first - 1)
..removeLast();
// Update current DateTime here
});
_controller.jumpToPage(_initialIndex);
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
_prev();
},
),
),
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: () {
_next();
},
),
),
],
),
body: PageView.builder(
controller: _controller,
itemCount: _list.length,
itemBuilder: (context, i) {
// This is where you should put your widget that generates
// the view of the month.
// Calculate DateTime like so 'DateTime(initialDate.year, initialDate.month + _list[index], initialDate.day)'
return Padding(
padding: const EdgeInsets.all(32.0),
child: Container(
color: Colors.blueAccent[100],
alignment: Alignment.center,
child: Text(
'${_list[i]}',
style: const TextStyle(fontSize: 32),
),
),
);
}
),
);
}
I propose you the code below that works.
setState()
methods.But it has the following caveat.
onPageChanged()
keeps the
page 0 so just after inserting it you go to the newly added page
because it is page 0. And since you already are on page 0, you can
only add new pages if you go forward and back again.Hope it helps
import 'package:flutter/material.dart';
void main() => runApp(LimeApp());
class LimeApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Pageview Test',
home: MainPage(),
);
}
}
int _lowerCount = -1;
int _upperCount = 1;
class MainPage extends StatefulWidget {
@override
MainPageState createState() {
return new MainPageState();
}
}
class MainPageState extends State<MainPage> {
List<Widget> _pages = <Widget>[
new Center(child: new Text("-1", style: new TextStyle(fontSize: 60.0))),
new Center(child: new Text("0", style: new TextStyle(fontSize: 60.0))),
new Center(child: new Text("1", style: new TextStyle(fontSize: 60.0)))
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.symmetric(
vertical: 50.0,
),
child: PageView(
onPageChanged: (pageId) {
if (pageId == _pages.length - 1) {
print("Last page, add page to end");
_upperCount = _upperCount + 1;
_pages.add(new Center(child: new Text(_upperCount.toString(), style: new TextStyle(fontSize: 60.0))));
setState(() {});
}
if (pageId == 0) {
print("First page, add page to start");
_lowerCount = _lowerCount - 1;
Widget w = new Center(child: new Text(_lowerCount.toString(), style: new TextStyle(fontSize: 60.0)));
_pages = [w]..addAll(_pages);
setState(() {});
}
},
controller: PageController(
initialPage: 1,
),
children: _pages,
),
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With