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

Flutter:SpectatorPage如何获取并同步ChatPage的chatScreenContainer组件

问题需求

需要实现两个核心功能:

  1. SpectatorPage 需获取所有 ChatPage 组件的 chatScreenContainer 方法返回的 Widget;
  2. 当 ChatPage 重建 chatScreenContainer 时,SpectatorPage 能同步显示更新后的内容。
解决方案

核心思路是将ChatPage中控制聊天界面的状态抽离为可监听的共享模型,让SpectatorPage和ChatPage依赖同一状态源,状态变化时自动同步UI。以下是具体实现步骤和修改后的代码:

1. 创建共享状态模型

定义ChatStateModel继承ChangeNotifier,封装所有控制聊天界面的状态,并在状态变化时通知监听者:

import 'package:flutter/foundation.dart';
import 'dart:math';

class ChatStateModel extends ChangeNotifier {
  bool isYourTurn = Random().nextBool();
  List<String> messagesStack = [];
  List<Widget> widgetStack = [];
  List<String> currentProps = [];

  // 发送消息并更新状态
  void sendMessage(String message) {
    messagesStack.add(message);
    widgetStack.add(Padding(
      padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
      child: Text(message, style: const TextStyle(color: Colors.white)),
    ));
    notifyListeners(); // 通知所有监听者状态已更新
  }

  // 其他修改状态的方法都需调用notifyListeners()
}

2. 修改ChatPage依赖共享状态

让ChatPage接收ChatStateModel实例,所有状态操作通过模型完成,确保状态统一:

import 'package:flutter/material.dart';

class ChatPage extends StatefulWidget {
  final ChatStateModel chatState;

  const ChatPage(this.chatState, {Key? key}) : super(key: key);

  @override
  State<ChatPage> createState() => ChatPageState();
}

class ChatPageState extends State<ChatPage> {
  @override
  void initState() {
    super.initState();
    widget.chatState.sendMessage('Salut ça va ?');
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: const Color.fromARGB(255, 236, 131, 166),
      child: Container(
        color: const Color.fromARGB(255, 236, 131, 166),
        width: 1000,
        height: double.infinity,
        child: Column(
          mainAxisSize: MainAxisSize.max,
          children: [
            const SizedBox(height: 36.0),
            chatScreenContainer(),
            const SizedBox(height: 4.0),
            // 消息输入栏(保持原有UI逻辑)
            Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(24.0),
                color: Colors.pinkAccent,
              ),
              width: 340,
              height: 40,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  const SizedBox(width: 10),
                  Container(
                    decoration: BoxDecoration(
                      color: const Color.fromARGB(255, 236, 131, 166),
                      borderRadius: BorderRadius.circular(20.0),
                    ),
                    width: 160,
                    height: 16,
                    child: Padding(
                      padding: const EdgeInsets.only(top: 3.0, left: 6.0),
                      child: Text('...', style: TextStyle(color: const Color.fromARGB(255, 235, 2, 80),fontSize: 10.0, decoration: TextDecoration.none),),
                    ),
                  ),
                  const Padding(padding: EdgeInsets.symmetric(horizontal: 42.0)),
                  Container(
                    width: 30,
                    height: 30,
                    child: ElevatedButton(
                      onPressed: () {
                        setState(() {
                          showDialog(
                            context: context,
                            builder: (context) {
                              return AlertDialog(
                                content: Container(
                                  alignment: Alignment.center,
                                  decoration: BoxDecoration(
                                    color: Colors.white,
                                    borderRadius: BorderRadius.circular(24.0),
                                  ),
                                  child: const Column(children: []),
                                ),
                              );
                            });
                        }); 
                      },
                      child: const Icon(Icons.lightbulb, color: Colors.yellow, size: 16),
                      style: ElevatedButton.styleFrom(
                        elevation: 8.0,
                        shape: CircleBorder(),
                        padding: EdgeInsets.zero,
                        backgroundColor: const Color.fromARGB(255, 236, 131, 166),
                      ),
                    ),
                  ),
                  const SizedBox(width: 10),
                  Container(
                    width: 30,
                    height: 30,
                    child: ElevatedButton(
                      onPressed: () {
                        // 通过模型发送消息,触发状态更新
                        widget.chatState.sendMessage('新消息');
                      },
                      child: const Icon(Icons.send, color: Colors.white, size: 16,),
                        style: ElevatedButton.styleFrom(
                        elevation: 8.0,
                        shape: CircleBorder(),
                        padding: EdgeInsets.zero,
                        backgroundColor: const Color.fromARGB(255, 236, 131, 166),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 22.0),
            Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(24.0),
                color: Colors.pinkAccent,
              ),
              width: 360,
              height: 220,
              child: ListView(children: const <Widget>[]),
            ),
          ],
        ),
      ),
    );
  }

  Widget chatScreenContainer() {
    return SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(24.0),
          color: Colors.pinkAccent,
        ),
        margin: const EdgeInsets.all(16.0),
        width: 400,
        height: 460,
        child: Column(
          children: <Widget>[
            Container(
              width: 360,
              height: 440,
              child: ListView.builder(
                itemCount: widget.chatState.widgetStack.length,
                itemBuilder: ((context, index) => widget.chatState.widgetStack[index])
              ),
            )
          ],
        ),
      ),
    );
  }
}

3. 修改SpectatorPage同步显示状态

SpectatorPage创建对应的共享状态实例,通过Consumer监听状态变化,自动更新UI:

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

class Game {
  List<dynamic> players; // 根据实际业务定义Player类型
  Game(this.players);
}

class SpectatorPage extends StatefulWidget {
  final Game game;
  SpectatorPage(Key? key, this.game) : super(key: key);

  @override
  State<SpectatorPage> createState() => SpectatorPageState();
}

class SpectatorPageState extends State<SpectatorPage> {
  late List<ChatStateModel> chatStateModels;
  ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    // 为每个玩家初始化对应的聊天状态模型
    chatStateModels = List.generate(widget.game.players.length, (_) => ChatStateModel());
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      controller: _scrollController,
      child: Container(
        alignment: Alignment.center,
        padding: const EdgeInsets.all(16.0),
        child: GridView.builder(
          shrinkWrap: true, // 适配SingleChildScrollView的嵌套布局
          physics: const NeverScrollableScrollPhysics(), // 禁用GridView自身滚动
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 16.0,
            mainAxisSpacing: 16.0,
            childAspectRatio: 0.85,
          ),
          itemCount: widget.game.players.length,
          itemBuilder: (context, index) {
            return ChangeNotifierProvider.value(
              value: chatStateModels[index],
              child: Consumer<ChatStateModel>(
                builder: (context, model, child) {
                  // 复用ChatPage的chatScreenContainer布局逻辑,确保UI一致
                  return SingleChildScrollView(
                    scrollDirection: Axis.vertical,
                    child: Container(
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(24.0),
                        color: Colors.pinkAccent,
                      ),
                      margin: const EdgeInsets.all(16.0),
                      width: double.infinity,
                      height: 460,
                      child: Column(
                        children: <Widget>[
                          Container(
                            width: double.infinity,
                            height: 440,
                            padding: const EdgeInsets.symmetric(horizontal: 20.0),
                            child: ListView.builder(
                              itemCount: model.widgetStack.length,
                              itemBuilder: ((context, idx) => model.widgetStack[idx])
                            ),
                          )
                        ],
                      ),
                    ),
                  );
                },
                // 如果需要同时显示完整的ChatPage,可取消注释下面一行
                // child: ChatPage(chatStateModels[index]),
              ),
            );
          },
        ),
      ),
    );
  }
}

注意事项

  • 需要在pubspec.yaml中添加provider依赖:
    dependencies:
      flutter:
        sdk: flutter
      provider: ^6.0.5
    
  • GridView嵌套在SingleChildScrollView时,必须设置shrinkWrap: true和physics: NeverScrollableScrollPhysics(),避免布局冲突;
  • 所有修改ChatStateModel状态的操作必须调用notifyListeners(),否则无法触发UI更新;
  • 如果不需要显示完整的ChatPage,可以直接在SpectatorPage中复用chatScreenContainer的布局逻辑,减少不必要的UI渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 02:30:56