我当前正在制作一个日历应用程序,我想向左或向右滑动以转到下个月或上个月。我正在使用PageView,方法是首先设置一个包含3个项目的数组,而初始页面是第二个数组。我想向右滑动并在页面末尾添加一个页面。我想向左滑动并将页面添加到开头。目前,如果您转到右侧(在页面末尾添加页面),则效果很好。但是,如果您转到左侧(在页面开头添加页面),则会出现一些奇怪的现象,并且根本无法正常工作。
我在下面用计数器粘贴了一个简单的示例。我不确定我做对了还是逻辑不对。谁能告诉我正确的方法?
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,
),
),
);
}
}
答案 0 :(得分:1)
我建议您使用下面的代码。
setState()
方法。但是有以下警告。
onPageChanged()
会保留
第0页,因此插入后便会转到新添加的页面
因为它是第0页。而且由于您已经在第0页上,因此您可以
仅在前进后退时才添加新页面。希望有帮助
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,
),
),
);
}
}
答案 1 :(得分:0)
我到了这一点,我有点亲密,但还不够亲密...
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 {
MainPage({
Key key,
}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final List<int> _pages = <int>[-1, 0, 1];
final PageController pageController = PageController(initialPage: 1);
Widget _buildPages(page) {
return Center(
child: Text(page.toString(), style: new TextStyle(fontSize: 60.0)),
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
pageController.addListener(() {
print("inside listener");
});
}
@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;
setState(() {
_pages.add(_upperCount);
});
}
if (pageId == 0) {
print("First page, add page to start");
_lowerCount = _lowerCount - 1;
setState(() {
_pages.insert(0, _lowerCount);
});
pageController.jumpToPage(1);
}
print(_pages);
},
controller: pageController,
children: List<Widget>.generate(_pages.length, (index) {
return _buildPages(_pages[index]);
}),
),
),
);
}
}
答案 2 :(得分:0)
我有类似的问题。我决定将解决方案与SetState->更新(重建)页面列表一起使用(在开头或结尾添加新内容),并设置当前索引以保持当前页面可见。问题是在页面之前触发的_onPageChanged方法已完全更改(这意味着该方法已触发DURNING动画)。 SetState导致从未完成的动画跳转到显示静态页面。我使用动画侦听器测试了解决方案(以查找动画的结尾),但是这种方法在完全无法预测的时刻被解雇了。 最后,我将_onPageChanged-> SetState与页面列表一起使用,但是我的列表扩展到15个元素-从今天到+-7天,我的动画效果都很流畅。下一个不顺利,另一个7确定:) 我的用户通常从当天算起的7天之内不会移动,但是我对这种解决方案并不感到骄傲。也许有人有更好的解决方案?
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:gymprogress/page-dashboard-widget.dart';
import 'package:gymprogress/chooseDateFromCalendar.dart';
import 'package:gymprogress/page-dashboard-timeline.dart';
import 'package:gymprogress/globals.dart';
import 'package:gymprogress/reusable/pageTemplate.dart';
import 'package:random_color/random_color.dart';
class Dashboard extends StatefulWidget {
Dashboard({Key key}) : super(key: key);
@override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
List<DashboardPageWidget> _daysList = [];
List<DashboardPageWidget> daysList = [];
DateTime _currentDate;
int _currentPageIndex;
int offset = 7;
PageController _controller = PageController(initialPage: 7, keepPage: false );
final List<ColorHue> _hueType = <ColorHue>[
ColorHue.green,
ColorHue.red,
ColorHue.pink,
ColorHue.purple,
ColorHue.blue,
ColorHue.yellow,
ColorHue.orange
];
ColorBrightness _colorLuminosity = ColorBrightness.veryLight;
ColorSaturation _colorSaturation = ColorSaturation.highSaturation;
@override
void initState() {
_currentDate = DateTime.now();
super.initState();
_goToDate(_currentDate);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
child: Stack(
children: <Widget>[
ScaffoldTemplate(body: body()),
],
),
);
}
Widget body() {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
actionPanel(),
Timeline(
date: _currentDate,
onPrevButton: () => _prevPressed(),
onNextButton: () => _nextPressed(),
),
Expanded(
child: bodyPageController()
),
],
),
);
}
Widget bodyPageController() {
return PageView(
scrollDirection: Axis.horizontal,
controller: _controller,
onPageChanged: (index) {_onPageChanged(index);
},
children: _daysList,
);
}
_myRandomColor(){
return RandomColor().randomColor(
colorHue: ColorHue.multiple(colorHues: _hueType),
colorSaturation: _colorSaturation,
colorBrightness: _colorLuminosity);
}
Widget actionPanel(){
return Container(
width: screenWidth,
// height: 50,
color: Color(0xFF068FFA),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 2, 0, 2),
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: Column(
children: <Widget>[
IconButton(
tooltip: 'Dodaj swój nowy trening',
icon: FaIcon(FontAwesomeIcons.calendarPlus, color: Colors.white,),
color: Colors.red,
onPressed: () => _goToDate(DateTime.now()),
),
],
),
),
),
Expanded(
flex: 1,
child: Container(
child: Column(
children: <Widget>[
IconButton(
tooltip: 'Przejdź do dnia dzisiejszego',
icon: FaIcon(FontAwesomeIcons.calendarCheck, color: Colors.white,),
color: Colors.red,
onPressed: () => _goToDate(DateTime.now()),
),
],
),
),
),
Expanded(
flex: 1,
child: Container(
child: Column(
children: <Widget>[
IconButton(
tooltip: 'Wybierz datę z kalendarza',
icon: FaIcon(FontAwesomeIcons.calendarAlt, color: Colors.white,),
color: Colors.red,
onPressed: () => _openCalendar(),
),
],
),
),
),
],
),
)
);
}
_prevPressed(){
setState(() {
_controller.animateToPage(_currentPageIndex-1, duration: Duration(milliseconds: 200), curve: Curves.ease);
});
}
_nextPressed(){
setState(() {
_controller.animateToPage(_currentPageIndex+1, duration: Duration(milliseconds: 200), curve: Curves.ease);
});
}
_onPageChanged(index){
_currentPageIndex = index;
if((_currentPageIndex<1) || (_currentPageIndex>(2*offset-1))){
_goToDate(_daysList[index].day.date);
}
setState(() {
_currentDate = _daysList[_currentPageIndex].day.date;
});
}
_goToDate(DateTime _date) async{
await _buildPageView(_date);
_controller.jumpToPage(offset);
}
_buildPageView(_date) {
DateTime date = DateTime(_date.year, _date.month, _date.day);
daysList = [];
for (var i=0; i<(2*offset+1); i++){
daysList.add(DashboardPageWidget(day: Day(date.add(Duration(days: i-offset)), _myRandomColor()),));
print(i.toString());
}
setState(() {
_daysList = daysList;
_currentDate = date;
_currentPageIndex = offset;
});
}
_openCalendar() async{
await Navigator.of(context).push(
PageRouteBuilder(
opaque: false, // set to false
pageBuilder: (_, __, ___) => SelectDateFromCalendar(),
)).then((value){
print(value);
_goToDate(value);
});
}
}
class Day{
DateTime date;
Color color;
Day(DateTime date, Color color){
this.date = date;
this.color = color;
}
}
答案 3 :(得分:0)
我想我已经成功了。我没有使用 onPageChange
事件。相反,我听取了控制器中的变化。
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),
),
),
);
}
),
);
}