You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中TextInputSettingsTile触发setState调用时机错误求助

问题:TextInputSettingsTile值变化时触发“setState() or markNeedsBuild() called during build.”错误

我是Flutter新手,正在使用flutter_settings_screens包开发应用的设置模块。当TextInputSettingsTile的值发生变化时,出现了“setState() or markNeedsBuild() called during build.”错误,查阅大量资料仍未理解问题所在,附上相关代码文件,请求提供解决方法。


main.dart 代码

import 'package:flutter/material.dart';
import 'pages/home.dart';
import 'pages/settings_page.dart';
import 'package:flutter_settings_screens/flutter_settings_screens.dart';


Future main() async {
  await Settings.init(cacheProvider: SharePreferenceCache());
  
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const LoginPage(title: 'TEST'),
    );
  }
}

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<LoginPage> createState() => LoginPageState();
}

class LoginPageState extends State<LoginPage> {
  final _username = TextEditingController();
  final _password = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('TEST'),
        actions: <Widget>[
          IconButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => SettingsPage()),
                );
              },
              icon: const Icon(Icons.settings))
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.fromLTRB(
                  20, 0, 20, 10),
              child: TextField(
                controller: _username,
                decoration: InputDecoration(
                    icon: const Icon(Icons.email),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(15.0),
                    ),
                    labelText: 'username'),
              ),
            ),
            Padding(
                padding: const EdgeInsets.fromLTRB(
                    20, 5, 20, 10),
                child: TextField(
                  controller: _password,
                  obscureText: true,
                  enableSuggestions: false,
                  autocorrect: false,
                  decoration: InputDecoration(
                      icon: const Icon(Icons.password_rounded),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(15.0),
                      ),
                      labelText: 'password'),
                )),
            ElevatedButton(
                onPressed: () => {},
                child: const Text('LOGIN'))
          ],
        ),
      ),
    );
  }
}

设置主页面代码

import 'package:flutter/material.dart';
import 'package:flutter_settings_screens/flutter_settings_screens.dart';
import 'settings/connection_page.dart';


class SettingsPage extends StatefulWidget {
  @override
  SettingsPageState createState() => SettingsPageState();
}

class SettingsPageState extends State<SettingsPage> {
  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
      title: const Text('Settings')
    ),
        body: SafeArea(
          child: ListView(
            padding: const EdgeInsets.all(12),
            children: [
              const SizedBox(height: 5,),
              SettingsGroup(
                  title: 'GENERAL', children: const <Widget>[
                    ConnectionPage()
                  ]
              ),
            ],
          ),
        ),
      );
}

报错的设置子页面代码

import 'package:flutter/material.dart';
import 'package:flutter_settings_screens/flutter_settings_screens.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

class ConnectionPage extends StatelessWidget {
  const ConnectionPage({Key? key}) : super(key: key);
  static const keyServer = 'key-server';

  @override
  Widget build(BuildContext context) => SimpleSettingsTile(
        title: 'Connection',
        subtitle: 'Parameters',
        leading: const FaIcon(FontAwesomeIcons.clipboardList),
        child: SettingsScreen(
          title: 'Connection',
          children: <Widget>[
            buildServer(),
          ],
        ),
      );

  Widget buildServer() => TextInputSettingsTile(
        settingKey: keyServer,
        title: 'Server',
        initialValue: ''
      );
}

解决办法

这个错误的核心是在Widget的build流程还未完成时,就触发了状态更新(setState或markNeedsBuild),针对你的代码,有两种可行的修复方式:

方式一:将ConnectionPage改为StatefulWidget并延迟初始化逻辑

把ConnectionPage从Stateless改为Stateful,利用WidgetsBinding.instance.addPostFrameCallback确保在build完成后再处理设置项的初始化或更新,避免在build期间触发状态变化:

import 'package:flutter/material.dart';
import 'package:flutter_settings_screens/flutter_settings_screens.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

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

  @override
  State<ConnectionPage> createState() => _ConnectionPageState();
}

class _ConnectionPageState extends State<ConnectionPage> {
  static const keyServer = 'key-server';
  bool _isInitialized = false;

  @override
  void initState() {
    super.initState();
    // 等待当前build流程完成后再处理
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!_isInitialized) {
        Settings.setValue(keyServer, '');
        setState(() {
          _isInitialized = true;
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    if (!_isInitialized) {
      // 初始化完成前显示占位组件
      return const SizedBox.shrink();
    }
    
    return SimpleSettingsTile(
      title: 'Connection',
      subtitle: 'Parameters',
      leading: const FaIcon(FontAwesomeIcons.clipboardList),
      child: SettingsScreen(
        title: 'Connection',
        children: <Widget>[
          TextInputSettingsTile(
            settingKey: keyServer,
            title: 'Server',
            initialValue: ''
          ),
        ],
      ),
    );
  }
}

方式二:使用ValueListenableBuilder监听设置值变化

通过Settings.getValueListenable获取值的监听对象,用ValueListenableBuilder包裹TextInputSettingsTile,确保状态更新在正确的时机触发:

修改ConnectionPage的buildServer方法:

Widget buildServer() {
  final serverValueListenable = Settings.getValueListenable<String>(keyServer, defaultValue: '');
  
  return ValueListenableBuilder<String>(
    valueListenable: serverValueListenable,
    builder: (context, value, child) {
      return TextInputSettingsTile(
        settingKey: keyServer,
        title: 'Server',
        initialValue: value,
        onChanged: (newValue) {
          // 确保更新操作不在build期间执行
          WidgetsBinding.instance.addPostFrameCallback((_) {
            Settings.setValue(keyServer, newValue);
          });
        },
      );
    },
  );
}

额外注意事项

  • 确保flutter_settings_screens包是最新版本,旧版本可能存在这类内部状态管理的bug;
  • 不要在StatelessWidget的build方法中直接触发状态更新或异步操作,这类逻辑尽量放在StatefulWidget的initState、didChangeDependencies或回调中。

内容的提问来源于stack exchange,提问作者federico__

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 10:45:32