【问题标题】:Adding new pages to a PageView while swiping滑动时将新页面添加到 PageView
【发布时间】:2018-09-30 15:26:32
【问题描述】:

我目前正在制作一个日历应用程序,我想向右或向左滑动以转到下个月或上个月。我通过最初设置一个包含 3 个项目的数组来使用 PageView,初始页面是第二个。我想向右滑动并在末尾添加一个页面。我想向左滑动并在开头添加一个页面。目前,如果您向右(将页面添加到末尾),效果很好。但是如果你向左(在开头添加页面)会有一些奇怪的行为,它根本不起作用。

我在下面粘贴了一个带有计数器的简单示例。我不确定我是否做得对,或者我的逻辑是否错误。谁能告诉我正确的方法吗?

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

【问题讨论】:

  • 如果你能更详细地解释一下当你向左滑动时它在哪些方面不起作用。
  • 这是我的问题,我不知道为什么当我向左滑动时它不起作用。我认为当您在 code_pages.insertcode 数组的开头插入时,pageView 可能会感到困惑。系统认为我正在查看索引 0,但是当我在开头插入时,我实际上正在查看索引 1。
  • 当您说“它不起作用”时,究竟发生了什么?没有什么?你就是不能向左滑动?
  • 哦,是的。抱歉,我以为你可以在我添加的 gif 中看到这一点。左侧的蓝色指示器表示我无法再滑动。
  • 我相信这个问题没有添加 gif ;/

标签: flutter


【解决方案1】:

我想我成功了。我没有使用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),
            ),
          ),
        );
      }
    ),
  );
}




【讨论】:

  • 哈利路亚!在我的地方工作。花很多时间让它与 onPageChanged 一起工作。谢谢,伙计。
【解决方案2】:

我建议您使用下面的代码。

  • 它必须使用有状态小部件才能使用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,
        ),
      ),
    );
  }
}

【讨论】:

    【解决方案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 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]);
              }),
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 这个特定示例使用了 StatelessWidget,所以我认为不需要这样做,因为当我转到右侧的较高数字时它工作得很好。我已经在 StatefulWidget 中尝试过并使用了 setState,但同样的问题仍然存在。
    • @casmang 我认为您必须将其转换为有状态的小部件。修改 _pages 后页面未正确呈现。尝试将其转换为 statefull 小部件并使用上面的代码,看看它是如何进行的
    • 非常接近,谢谢!如果我想不出更好的东西,这可能会奏效。
    • 看看我的建议,重新创建列表,新页面在前。
    【解决方案4】:

    我有类似的问题。我决定使用带有 SetState 的解决方案 -> 更新(重建)页面列表(在开始或结束时添加新的)并设置当前索引以保持当前页面可见。问题是页面完全更改之前触发的 _onPageChanged 方法(这意味着该方法在动画期间被触发)。 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;
          }
        }
    

    【讨论】:

    • 你能分享一下输出吗
    猜你喜欢
    • 2021-05-20
    • 2020-07-05
    • 1970-01-01
    • 2021-07-02
    • 2019-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多