无法在提供者中的容器外部设置状态 [英] Cannot set state outside a Container in Provider

查看:100
本文介绍了无法在提供者中的容器外部设置状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道问题出在生命周期中,我试图在呈现Widget之前在Provider中设置状态,但是在哪里可以做到这一点.仅在容器小部件中?但是除非有按钮或其他东西,否则我无法做到这一点.

I understand that the problem is in a lifecircle that I'm trying to set a state in Provider before the Widget is rendered but where can I do that. Only in a Container Widget? But I cannot do that unless I've a button or something.

我希望您在这里遇到了问题.

I hope you got the issue of the problem here.

任何提示,我将不胜感激!

I would appreciate any hints!

我的错误:

setState() or markNeedsBuild() called during build.

The setter 'lastPage=' was called on null.
Receiver: null
Tried calling: lastPage=true

如果我在这里设置状态

  _detectLastPage() {
    int currentPage = this.currentStep == null ? 1 : this.currentStep + 1;

    if (currentPage == 1 && this.currentStep == null) {
      this._onFirstPage();
    } else if (currentPage == this.totalSteps) {
      this.lastPage = true;
      _welcomeBloc.lastPage = true;
      this._onLastPage();
    } else {
      this.lastPage = false;
      _welcomeBloc.lastPage = true;
    }
  }

我的窗口小部件:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:ui_flutter/screens/welcome/welcome_bloc.dart';

class Footer extends StatelessWidget {
  final int currentStep;
  final int totalSteps;
  final Color activeColor;
  final Color inactiveColor;
  final Duration duration;
  final Function onFinal;
  final Function onStart;

  final double radius = 10.0;
  final double distance = 4.0;

  Container stepper;
  Container nextArrow;
  bool lastPage;
  WelcomeBloc _welcomeBloc;

  Footer({
    this.activeColor,
    this.inactiveColor,
    this.currentStep,
    this.totalSteps,
    this.duration,
    this.onFinal,
    this.onStart,
  }) {
    this._detectLastPage();
    this._makeStepper();
    this._makeNextArrow();
  }

  @override
  Widget build(BuildContext context) {
    print('footer is launching');

    final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
    _welcomeBloc = _welcome;
    // this._welcomeBloc.lastPage = true; // I'd like to set the state here

    return Container(
      alignment: Alignment.bottomCenter,
      padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          this.stepper,
          this.nextArrow,
          RaisedButton(
            child: Text('kdfljds'),
            onPressed: () {
              print(_welcomeBloc.lastPage);
              _welcomeBloc.lastPage = true; // I can access from here BUT CANNOT access outside this container
            },
          )
        ],
      ),
    );
  }

  _makeCirle(activeColor, inactiveColor, position, currentStep) {
    currentStep = currentStep ?? 0;
    Color color = (position == currentStep) ? activeColor : inactiveColor;

    return Container(
      height: this.radius,
      width: this.radius,
      margin: EdgeInsets.only(left: this.distance, right: this.distance),
      decoration: BoxDecoration(
          color: color,
          border: Border.all(color: activeColor, width: 2.0),
          borderRadius: BorderRadius.circular(50.0)),
    );
  }

  _makeStepper() {
    List<Container> circles = List();

    for (var i = 0; i < totalSteps; i++) {
      circles.add(
        _makeCirle(this.activeColor, this.inactiveColor, i, this.currentStep),
      );
    }

    this.stepper = Container(
      child: Row(
        children: circles,
      ),
    );
  }

  _makeNextArrow() {
    this.nextArrow = Container(
      child: Padding(
        padding: const EdgeInsets.only(right: 8.0),
        child: GestureDetector(
            onTap: () {
              _welcomeBloc.controller.nextPage(
                duration: this.duration ?? Duration(milliseconds: 500),
                curve: Curves.easeInOut,
              );
            },
            child: Icon(
              Icons.arrow_forward,
            )),
      ),
    );
  }

  _onLastPage() {
    if (this.onFinal != null) {
      this.onFinal();
    }
  }

  _onFirstPage() {
    if (this.onStart != null) {
      this.onStart();
    }
  }

  _detectLastPage() {
    int currentPage = this.currentStep == null ? 1 : this.currentStep + 1;

    if (currentPage == 1 && this.currentStep == null) {
      this._onFirstPage();
    } else if (currentPage == this.totalSteps) {
      this.lastPage = true;
      this._onLastPage();
    } else {
      this.lastPage = false;
    }
  }
}


BlocFile

import 'package:flutter/material.dart';

class WelcomeBloc extends ChangeNotifier {

  PageController _controller = PageController();
  int _currentPage;
  bool _lastPage = false;

  bool get lastPage => _lastPage; 

  set lastPage(bool value){
    print(value);
    _lastPage = value;
    notifyListeners();
  }

  int get currentPage => _currentPage;

  set currentPage(int value) {
    _currentPage = value;
    notifyListeners();
  }

  get controller => _controller;

  nextPage(Duration duration, Curves curve){
    controller.nextPage(duration: duration, curve: curve);
  }
}

[![error screen with StateLess, since I use Provider][1]][1]

我在这里这样称呼:

_detectLastPage() {
    int currentPage =
        this.widget.currentStep == null ? 1 : this.widget.currentStep + 1;

    if (currentPage == 1 && this.widget.currentStep == null) {
      this._onFirstPage();
    } else if (currentPage == this.widget.totalSteps) {
      this.lastPage = true;
      setState(() {
        _welcomeBloc.lastPage = true;
      });
      this._onLastPage();
    } else {
      this.lastPage = false;
      setState(() {
        _welcomeBloc.lastPage = false;
      });
    }
  }

而且没有SetState似乎是同样的错误...

And without SetState seem to be the same error...

如果我从您的示例的initState内部调用,则会出现此错误.只是忘了你附上它

this error if I call from inside initState from your example. Just forgot you attach it

推荐答案

如果我做对了,您正在尝试通过一些下面的圆圈(指示符)来模拟PageView导航.

If I got it right you are trying to simulate PageView navigation by some circle bellow it(Indicators).

要这样做,有很多好的资源,还有一些类似的软件包: 此示例此程序包

To do so there are lots of good resources and also packages like: This example or this package

但是对于您的代码,我用两种方法编写了它:

But for your code I wrote it in 2 approaches:

第一种方法

这是您的代码和使用提供程序.

This one is your code and use provider.


import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(Home());
}

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (BuildContext context) => WelcomeBloc(),
      child: Consumer<WelcomeBloc>(
        builder: (BuildContext context, value, Widget child) {
          PageController controller = value.controller;
          print('object');
          return MaterialApp(
            home: Scaffold(
              body: Stack(
                children: <Widget>[
                  PageView(
                    controller: controller,
                    children: List.generate(
                        10, (i) => Center(child: Text('Page $i'))),
                    onPageChanged: (i) {
                      value.currentPage = i;
                    },
                  ),
                  Footer(
                    activeColor: Colors.red,
                    duration: Duration(seconds: 1),
                    inactiveColor: Colors.yellow,
                    onFinal: () {},
                    onStart: () {},
                    totalSteps: 10,
                  )
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

class Footer extends StatefulWidget {
  final int totalSteps;
  final Color activeColor;
  final Color inactiveColor;
  final Duration duration;
  final Function onFinal;
  final Function onStart;
  final double radius;
  final double distance;

  Footer({
    this.activeColor,
    this.inactiveColor,
    this.totalSteps,
    this.duration,
    this.onFinal,
    this.onStart,
    this.radius = 10.0,
    this.distance = 4.0,
  });

  @override
  _FooterState createState() => _FooterState();
}

class _FooterState extends State<Footer> {
  bool lastPage;
  WelcomeBloc _welcomeBloc;

  @override
  Widget build(BuildContext context) {
    final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);

    _welcomeBloc = _welcome;

    // this._welcomeBloc.lastPage = true; // I'd like to set the state here

    return Container(
      alignment: Alignment.bottomCenter,
      padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          _makeStepper(),
          _makeNextArrow(),
        ],
      ),
    );
  }

  _makeCircle(activeColor, inactiveColor, position, currentStep) {
    currentStep = currentStep ?? 0;
    Color color = (position == currentStep) ? activeColor : inactiveColor;

    return Container(
      height: widget.radius,
      width: widget.radius,
      margin: EdgeInsets.only(left: widget.distance, right: widget.distance),
      decoration: BoxDecoration(
          color: color,
          border: Border.all(color: activeColor, width: 2.0),
          borderRadius: BorderRadius.circular(50.0)),
    );
  }

  _makeNextArrow() {
    return Container(
      child: Padding(
        padding: const EdgeInsets.only(right: 8.0),
        child: GestureDetector(
            onTap: () async {
              await _welcomeBloc.nextPage(widget.duration, Curves.easeInOut);
              setState(() {});
            },
            child: Icon(
              Icons.arrow_forward,
            )),
      ),
    );
  }

  _makeStepper() {
    return Container(
      child: Row(
        children: List.generate(
          widget.totalSteps,
          (i) => _makeCircle(
            this.widget.activeColor,
            this.widget.inactiveColor,
            i,
            _welcomeBloc.currentPage,
          ),
        ),
      ),
    );
  }

  _onLastPage() {
    if (this.widget.onFinal != null) {
      this.widget.onFinal();
    }
  }

  _onFirstPage() {
    if (this.widget.onStart != null) {
      this.widget.onStart();
    }
  }

  _detectLastPage() {
    int currentPage =
        _welcomeBloc.currentPage == null ? 1 : _welcomeBloc.currentPage + 1;

    if (currentPage == 1 && _welcomeBloc.currentPage == null) {
      this._onFirstPage();
    } else if (currentPage == this.widget.totalSteps) {
      this.lastPage = true;
      this._onLastPage();
    } else {
      this.lastPage = false;
    }
  }
}

class WelcomeBloc extends ChangeNotifier {
  final PageController _controller = PageController();
  int _currentPage = 0;
  bool _lastPage = false;

  bool get lastPage => _lastPage;

  set lastPage(bool value) {
    _lastPage = value;
    notifyListeners();
  }

  int get currentPage => _currentPage;

  set currentPage(int value) {
    _currentPage = value;
    notifyListeners();
  }

  PageController get controller => _controller;

  Future<void> nextPage(Duration duration, Curve curve) {
    currentPage = controller.page.floor() + 1;
    return controller.nextPage(duration: duration, curve: curve);
  }
}

第二种方法

在第二篇文章中,我删除了提供程序的内容,因为使用PageController功能可以在没有它的情况下完成它.

In the second one I removed provider stuff because it can be done without it by using PageController features.

import 'package:flutter/material.dart';

void main() {
  runApp(Home());
}

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  PageController controller = PageController(initialPage: 0);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Stack(
          children: <Widget>[
            PageView(
              controller: controller,
              children: List.generate(
                10,
                (i) => Center(child: Text('Page $i')),
              ),
              onPageChanged: (page) {
                setState(() {});
              },
            ),
            Footer(
              currentPage: controller.hasClients ? controller.page.floor() : 0,
              activeColor: Colors.red,
              inactiveColor: Colors.yellow,
              totalSteps: 10,
              onTap: () async {
                await controller.nextPage(
                  duration: Duration(seconds: 1) ?? Duration(milliseconds: 500),
                  curve: Curves.easeInOut,
                );
                setState(() {});
              },
            )
          ],
        ),
      ),
    );
  }
}

class Footer extends StatelessWidget {
  final int totalSteps;
  final Color activeColor;
  final Color inactiveColor;
  final double radius;
  final double distance;
  final int currentPage;
  final GestureTapCallback onTap;

  Footer({
    this.activeColor,
    this.inactiveColor,
    this.totalSteps,
    this.radius = 10.0,
    this.distance = 4.0,
    this.currentPage,
    this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.bottomCenter,
      padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          _makeStepper(),
          _makeNextArrow(),
        ],
      ),
    );
  }

  _makeCircle(activeColor, inactiveColor, position) {
    Color color = (position == currentPage) ? activeColor : inactiveColor;

    return Container(
      height: radius,
      width: radius,
      margin: EdgeInsets.only(left: distance, right: distance),
      decoration: BoxDecoration(
          color: color,
          border: Border.all(color: activeColor, width: 2.0),
          borderRadius: BorderRadius.circular(50.0)),
    );
  }

  _makeNextArrow() {
    return Container(
      child: Padding(
        padding: const EdgeInsets.only(right: 8.0),
        child: GestureDetector(
            onTap: onTap,
            child: Icon(
              Icons.arrow_forward,
            )),
      ),
    );
  }

  _makeStepper() {
    return Container(
      child: Row(
        children: List.generate(
          totalSteps,
          (i) => _makeCircle(
            this.activeColor,
            this.inactiveColor,
            i,
          ),
        ),
      ),
    );
  }
}

这篇关于无法在提供者中的容器外部设置状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆