滑动时将新页面添加到PageView

时间:2018-09-30 15:26:32

标签: flutter

我当前正在制作一个日历应用程序,我想向左或向右滑动以转到下个月或上个月。我正在使用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,
        ),
      ),
    );
  }
}

Swiping Example:

4 个答案:

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