Pageview 中的 Flutter 回调导致异常

Flutter Callback in Pageview causing Exception

我正在尝试创建一个综合浏览量,我可以将另一个文件中定义的小部件加载到其中。这工作正常,除了当我尝试添加回调时,我收到以下错误:

FlutterError(在构建过程中调用了 setState() 或 markNeedsBuild()。

当输入的电子邮件被认为有效时(即当email_entry.dart中的代码调用从account_onboarding.dart文件传递的回调函数时),将触发此错误。无法确定为什么会发生这种情况,并且似乎不存在关于此主题的教程。我对 Dart/Flutter 还是很陌生,所以我希望有人能指出这里发生的事情(和修复)。

这是我的代码:
-父小部件,account_onboarding.dart

import 'package:flutter/material.dart';
import 'package:page_view_indicators/page_view_indicators.dart';
import 'package:animated_title_screen/screens/email_entry.dart';

class AccountOnboarding extends StatefulWidget {
  const AccountOnboarding({Key? key}) : super(key: key);

  @override
  State<AccountOnboarding> createState() => _AccountOnboardingState();
}

class _AccountOnboardingState extends State<AccountOnboarding> {
  final _pController = PageController(initialPage: 0);
  final _currentPageNotifier = ValueNotifier<int>(0);
  final List<Widget> _pages = [];

  bool validEmail = false;                            //Callback should set this variable

  @override
  void initState() {
    super.initState();
    _pages.add(                                   //Add the EmailEntry widget to the list
      EmailEntry(emailIsValid: (p0) {
        setState(() {
          validEmail = p0;
        });
      },),
    );
    _pages.add(
      Container(
        color: Colors.blue,
        child: Text("Pg2"),
      ),
    );
    _pages.add(
      Container(
        color: Colors.green,
        child: Text("Pg3"),
      ),
    );
  }

  @override
  void dispose() {
    _pController.dispose();
    _currentPageNotifier.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          "Create Account",
          style: Theme.of(context).textTheme.headline5,
        ),
        centerTitle: true,
        actions: [
          IconButton(
            icon: const Icon(Icons.close),
            onPressed: () => Navigator.pop(context),
          ),
        ],
      ),
      body: Stack(
        fit: StackFit.expand,
        children: [
          Column(
            children: [
                Row(
                  children: [ 
                      Text(
                        "Step ${_currentPageNotifier.value + 1} of ${_pages.length}",
                      ),            
                      CirclePageIndicator(
                        dotColor: const Color(0xFF323232),
                        selectedDotColor: const Color(0xFFE4231F),
                        size: 10,
                        selectedSize: 10,
                        currentPageNotifier: _currentPageNotifier,
                        itemCount: _pages.length,
                      ),                   
                  ],
                ),
                PageView(
                  controller: _pController,
                  onPageChanged: (index) {
                    setState(() {
                      _currentPageNotifier.value = index;
                    });
                  },
                  children: [
                    for (Widget p in _pages) p,           //Display all pages in _pages[]
                  ],
                ),          
              ElevatedButton(
                child: const Text("Continue"),
                onPressed: () => print("Pressed 2"),
                style: ElevatedButton.styleFrom(
                  primary: validEmail ? const Color(0xFFE1251B) : Colors.black,
                  textStyle: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 15,
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

这里是 email_entry.dart 代码:

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

class EmailEntry extends StatefulWidget {
  final Function(bool) emailIsValid;                   //Function required in constructor
  const EmailEntry({Key? key, required this.emailIsValid}) : super(key: key); 

  @override
  State<EmailEntry> createState() => _EmailEntryState();
}

class _EmailEntryState extends State<EmailEntry> {
  final _emailController = TextEditingController();
  FocusNode _emailFocus = FocusNode();

  @override
  void initState() {
    super.initState();
    _emailController.addListener(() => setState(() {}));
    _emailFocus.addListener(() {
      print("Focus email");
    });
  }

  @override
  void dispose() {
    _emailController.dispose();
    super.dispose();
  }

  bool validateEmail(String email) {
    bool valid = RegExp(
            r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$")
        .hasMatch(email);
    if (valid) {
      widget.emailIsValid(true);                             //Call the callback function
    }
    return valid;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
            Column(
              children: [
                  Text(
                    "YOUR EMAIL",
                    style: Theme.of(context).textTheme.headline2,
                  ),
                  Text(
                      "Please use an email address that you would like to make your login.",
                      style: Theme.of(context).textTheme.bodyText2,
                      textAlign: TextAlign.center,
                    ),
                Container(                                    
                  child: Text(
                    "Email Address",                    
                  ),
                ),
                TextField(
                  controller: _emailController,
                  keyboardType: TextInputType.emailAddress,
                  focusNode: _emailFocus,
                  suffixIcon: getTextFieldSuffix(emailController, _emailFocus),  //OFFENDING CODE
                ),
              ],
            ),
        ],
      ),
    );
  }
//THIS FUNCTION CAUSED THE ISSUE.  It is code I got from a youtube //tutorial.  Probably should have guessed.
  Widget getTextFieldSuffix(TextEditingController controller, FocusNode node) {
    if (controller.text.isNotEmpty && node.hasFocus) {
      return IconButton(
          color: Colors.grey.withAlpha(150),
          onPressed: () => controller.clear(),
          icon: const Icon(Icons.close));
    } else if (controller.text.isNotEmpty && !node.hasFocus) {
      return const Icon(
        Icons.check,
        color: Colors.green,
      );
    }
    return Container(
      width: 0,
    );
  }
}

initState,你需要调用addPostFrameCallback。 像这样...

  @override
  void initState() {
    super.initState();
    ///Add This Line
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
      ///All of your code
    });
  }


我发现我的产品版本中有一些代码会在用户每次在电子邮件地址的文本字段中输入字母时调用重绘。这导致了一个问题,因为屏幕已经被重绘,而我正在调用 setState 再次重绘它。我将编辑上面显示的代码以包含有问题的代码。