Flutter 中 PageController 的 Provider 状态管理

Posted

技术标签:

【中文标题】Flutter 中 PageController 的 Provider 状态管理【英文标题】:Provider state management for PageController in Flutter 【发布时间】:2021-08-27 13:58:07 【问题描述】:

我用 PageController 做了一个网站来控制一些屏幕的滚动。我制作了一个带有屏幕名称的菜单部分,当按下其中任何一个时,用户将导航到相应的屏幕。

应用程序运行良好。但是我重构了菜单按钮,这样我就可以控制它的样式,并在将来添加动画。

但是当我重构按钮时,我无法传递 PageController 索引,或者 nextPage 函数。

这就是我想到使用提供程序包的原因。但是,我之前没有打包,而且我的设置似乎很复杂。因为我应该将 PageController 状态传递给按钮,并且按钮应该发送带有新编号的 nextPage 函数。

我怎样才能做到这一点?

这是我的代码:

按钮:

class NavigationButton extends StatefulWidget 

  const NavigationButton(
    Key key,
    this.title,
    // this.onPressed
  ) : super(key: key);

  final String title;
  // final VoidCallback onPressed;

  @override
  _NavigationButtonState createState() => _NavigationButtonState();


class _NavigationButtonState extends State<NavigationButton> 

  bool isHovering = false;

  @override
  Widget build(BuildContext context) 

    return InkWell(
      child: Container(
        decoration: BoxDecoration(
          border: isHovering == true
          ? Border(right: BorderSide(color: Colors.blueGrey, width: 10))
          : Border(right: BorderSide.none)
        ),
        child: Text(
          widget.title,
          style: TextStyle(
            color: Colors.white,
            fontSize: 25
          )
        )
      ),
      onHover: (value) 
        setState(() 
          isHovering = value;
        );
      ,
      onTap: () 
    );
  

主要布局:

class MainLayout extends StatefulWidget 
  @override
  _MainLayoutState createState() => _MainLayoutState();


class _MainLayoutState extends State<MainLayout> 

  int _index = 0;
  int selectedButton;
  bool isHovering = false;
  PageController pageController = PageController();

  final List<String> _sectionsName = [
    "Home",
    "About",
    "Services",
    "Portfolio",
    "Testimonials",
    "News",
    "Contact"
  ];


  @override
  Widget build(BuildContext context) 
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: MediaQuery.of(context).size.width > 760
      ? null
      : AppBar(
        elevation: 0.0,
        backgroundColor: Colors.transparent
      ),
      drawer: MediaQuery.of(context).size.width < 760 ? DrawerWidget() : null,
      body: Stack(
        children: [
          // MediaQuery.of(context).size.width < 760 ? Container() : DrawerWidget(),
          Listener(
            onPointerSignal: (pointerSignal) 
              if (pointerSignal is PointerScrollEvent) 
                if (pointerSignal.scrollDelta.dy > 0) 
                  if(_index < _sectionsName.length) 
                    // int newIndex = _index + 1;
                    // pageController.jumpToPage(newIndex);
                    pageController.nextPage(
                      curve: Curves.easeIn, duration: Duration(milliseconds: 500)
                    );
                  
                
                else
                
                  if(_index < _sectionsName.length) 
                    // int newIndex = _index - 1;
                    // pageController.jumpToPage(newIndex);
                    pageController.previousPage(
                      curve: Curves.easeIn, duration: Duration(milliseconds: 500)
                    );
                  
                
              
            ,
            child: PageView(
              controller: pageController,
              scrollDirection: Axis.vertical,
              physics: NeverScrollableScrollPhysics(),
              children: [
                HeroSection(),
                AboutSection(),
                ServicesSection(),
                Portfoliosection(),
                TestimonialsSection(),
                NewsSection(),
                ContactSection()
              ],
              onPageChanged: (index) 
                _index = index;
              
            )
          ),
          MediaQuery.of(context).size.width < 760
          ? Container()
          : Align(
            alignment: Alignment.centerRight,
            child: Container(
              height: 205,
              width: 200,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  for (int index = 0; index < _sectionsName.length; index++)
                  NavigationButton(title: _sectionsName[index])
                ]
              )
            )
          )
        ]
      )
    );
  

提前谢谢...

编辑:

我关注了@EdwynZN 的回答,这很有帮助。但是我必须像这样将索引从 MainLayout 传递给 NavigationButton:

主布局: NavigationButton(title: _sectionsName[index], index: index)

导航按钮: 将此添加到构造函数中:this.index。 而这个类:final int index;

终于:

onTap: () 
        controller.animateToPage(widget.index, curve: Curves.easeIn, duration: Duration(milliseconds: 500));
      

我希望有一天这会对某人有所帮助......

【问题讨论】:

你到底想完成什么?您希望 NavigationButton 在 PageController 更改时做出反应,并且在那里还有一个 PageController 的实例,以便您可以在 NavigationButton 中使用 nextPage ? (我想使用提供者) 感谢您的评论和您的时间...我将 NavigationButton 重构为另一个类,以便我可以设置它的样式。单击该按钮时,应导航到另一个屏幕(页面)。而且,按钮在被点击后应该会改变颜色。 【参考方案1】:

使用 Provider,您可以使用 ChangeNotifierProvider.value 包装需要 PageController 的小部件

ChangeNotifierProvider.value(
    value: pageController,
    Align(
        alignment: Alignment.centerRight,
        child: Container(
          height: 205,
          width: 200,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              for (int index = 0; index < _sectionsName.length; index++)
              NavigationButton(title: _sectionsName[index])
            ]
          )
        )
      ),
),

那么所有的NavigationButton都可以作为inheretedWidget访问pageController

class _NavigationButtonState extends State<NavigationButton> 

  bool isHovering = false;
  PageController controller;
  int index;

  @override
  void didChangeDependencies() 
    controller = Provider.of<PageController>(context); //you can read the pagecontroller here
    index = controller.page.round(); // will update when the page changes so you can do some logic with colors
    super.didChangeDependencies();
   

  @override
  Widget build(BuildContext context) 

    return InkWell(
      child: Container(
        decoration: BoxDecoration(
          border: isHovering == true
          ? Border(right: BorderSide(color: Colors.blueGrey, width: 10))
          : Border(right: BorderSide.none)
        ),
        child: Text(
          widget.title,
          style: TextStyle(
            color: Colors.white,
            fontSize: 25
          )
        )
      ),
      onHover: (value) 
        setState(() 
          isHovering = value;
        );
      ,
      onTap: () 
    );
  

【讨论】:

非常感谢您的帮助并花时间回答我的问题。我试过你的代码,但抛出了这个错误:NoSuchMethodError:null 上的无效成员:'round'。我试图自己解决它,但不能..我该如何解决这个问题? 另外,当我尝试将 ChangeNotifierProvider.value 放在树上时,提供者找不到它。我该如何解决这个问题?我在哪里可以学习和了解提供程序包?我之前找到的所有来源都没有完全传达主题,而且总是缺少一些东西。 我也尝试了一些东西,我想我应该和你分享一下:我将索引更改为 double,散列出 controller.page.round();,然后创建 int newIndex = index .toInt();和controller.animateToPage(newIndex,曲线:Curves.easeIn,持续时间:持续时间(毫秒:500));在 onTap 里面。但是,我收到此错误: NoSuchMethodError: invalid member on null: 'toInt' 我也在 didChangeDependencies(): index = 0; controller.addListener(() setState(() index = controller.page.toInt(); ); ); 我终于解决了..我会更新我的问题,我会将您的答案标记为已接受..但是,如果您从他们那里知道任何学习提供者的好资源,请分享他们和我一起。真是太感谢你了……

以上是关于Flutter 中 PageController 的 Provider 状态管理的主要内容,如果未能解决你的问题,请参考以下文章

在 Flutter 的 PageView 模块中没有正确设置 PageController。弹出职位非空错误

无法在 Flutter 中的 PageController 上跳转到页面

Flutter自定义Widget—随滑动改变高度的PageView

Flutter自定义Widget—随滑动改变高度的PageView

Flutter PageView 属性使用详解

Flutter - 如何制作 PageView 和 ListView?