flutter 使用TabBarView中显示的正确值创建新选项卡

uwopmtnx  于 2023-01-18  发布在  Flutter
关注(0)|答案(1)|浏览(153)

此Flutter代码通过WebSocket连接到ESP32,并显示从设备接收的数据。应用程序有一个选项卡栏,允许在不同连接的ESP32设备之间切换。我可以通过打开“添加工作站”对话框添加新的ESP32设备,在该对话框中我可以输入设备的名称、IP地址和端口。当我添加新设备时,为该设备创建了一个新的选项卡,应用程序使用提供的IP地址和端口通过WebSocket连接到设备。我还可以删除任何添加的设备。我遇到的问题是,当我添加新设备时,创建了一个新的选项卡,但ESP32的名称,这些值和重新连接按钮不显示在TabBarView中。
在这件事上我需要一些帮助。我不知道我做错了什么。
先谢谢你的帮助。
遵循代码:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:web_socket_channel/io.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'ESP3" Monitor',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

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

class Station {
  late final String name;
  late final String ipAddress;
  late final String port;
  double rangeData;
  double forceData;

  Station({
    required this.name,
    required this.ipAddress,
    required this.port,
    this.rangeData = 0.0,
    this.forceData = 0.0,
  });
}

class MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final List<Station> _stations = [];
  late TabController _tabController;
  late IOWebSocketChannel _channel;
  late TextEditingController _stationName;
  late TextEditingController _ipAddress;
  late TextEditingController _port;
  String? selectedTab = tabs[0].text;
  var tabName = "";

  static List<Tab> tabs = [
    const Tab(text: ""),
  ];

  @override
  void initState() {
    _stationName = TextEditingController();
    _ipAddress = TextEditingController();
    _port = TextEditingController();
    _tabController = TabController(vsync: this, length: tabs.length);
    super.initState();
  }

  @override
  void dispose() {
    _stationName.dispose();
    _ipAddress.dispose();
    _port.dispose();
    _tabController.dispose();
    super.dispose();
  }

  void _connectToEsp32(String ipAddress, String port, Station station) {
    _channel = IOWebSocketChannel.connect('ws://$ipAddress:$port');
    _channel.stream.listen(
      (data) {
        final Map<String, dynamic> receivedData = jsonDecode(data);
        setState(() {
          final int index = _stations.indexWhere((element) => element.name == station.name);
          if (index >= 0) {
            station.rangeData = receivedData['range'];
            station.forceData = receivedData['force'];
            _tabController.animateTo(index);
          }
        });
      },
    );
  }

  void _openAddStationDialog(BuildContext context) {
    _stationName.text = "";
    _ipAddress.text = "";
    _port.text = "";
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Add Station'),
          content: Form(
            key: _formKey,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                TextFormField(
                  decoration: const InputDecoration(
                    suffixIcon: Icon(Icons.list),
                    labelText: 'Station Name',
                  ),
                  controller: _stationName,
                  validator: (value) {
                    tabName = value!;
                    if (value.isEmpty) {
                      return 'Please enter a Station Name';
                    }
                    return null;
                  },
                ),
                TextFormField(
                  decoration: const InputDecoration(
                    suffixIcon: Icon(Icons.computer),
                    labelText: 'IP Address',
                  ),
                  controller: _ipAddress,
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please enter IP address';
                    }
                    return null;
                  },
                ),
                TextFormField(
                  decoration: const InputDecoration(
                    suffixIcon: Icon(Icons.settings_applications),
                    labelText: 'Port',
                  ),
                  controller: _port,
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please enter Port';
                    }
                    return null;
                  },
                ),
              ],
            ),
          ),
          actions: <Widget>[
            ElevatedButton.icon(
              onPressed: () {
                setState(() {});
                if (_formKey.currentState!.validate()) {
                  final station = Station(
                    name: _stationName.text,
                    ipAddress: _ipAddress.text,
                    port: _port.text,
                  );
                  _stations.add(station);
                  tabs.add(Tab(text: tabName));
                  _connectToEsp32(station.ipAddress, station.port, station);
                  Navigator.pop(context);
                }
              },
              icon: const Icon(
                Icons.add,
              ),
              label: const Text('Add'),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      initialIndex: 0,
      length: tabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Station Monitor'),
          actions: <Widget>[
            ElevatedButton.icon(
              onPressed: () {
                _openAddStationDialog(context);
              },
              icon: const Icon(
                Icons.keyboard_control_rounded,
              ),
              label: const Text('Add Station'),
            ),
            ElevatedButton.icon(
              onPressed: () {
                showDialog(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: const Text("Select tab to remove"),
                      content: tabs.isNotEmpty
                          ? StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
                              return DropdownButton<String>(
                                items: tabs.map((tab) {
                                  return DropdownMenuItem<String>(
                                    value: tab.text,
                                    child: Text(tab.text ?? ""),
                                  );
                                }).toList(),
                                onChanged: (String? value) {
                                  setState(() {
                                    selectedTab = value;
                                  });
                                },
                                value: selectedTab,
                              );
                            })
                          : Container(),
                      actions: <Widget>[
                        ElevatedButton(
                          child: const Text("Remove"),
                          onPressed: () {
                            if (tabs.isNotEmpty) {
                              setState(() {
                                int index = tabs.indexWhere((tab) => tab.text == selectedTab);
                                tabs.removeAt(index);
                                selectedTab = tabs.isNotEmpty ? tabs[0].text : null;
                              });
                              Navigator.of(context).pop();
                            }
                          },
                        ),
                      ],
                    );
                  },
                );
              },
              icon: const Icon(Icons.remove),
              label: const Text('Remove Station'),
            ),
          ],
          bottom: TabBar(tabs: tabs),
        ),
        body: TabBarView(
          controller: _tabController,
          children: _stations.map((station) {
            return Column(
              children: <Widget>[
                Column(children: <Widget>[
                  const SizedBox(
                    height: 30,
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Text("Station Name: ${station.name}"),
                      const SizedBox(width: 10),
                      PopupMenuButton<int>(
                        itemBuilder: (context) => [
                          const PopupMenuItem(
                            value: 1,
                            child: Text("Reconnect"),
                          ),
                        ],
                        onSelected: (value) {
                          if (value == 1) {
                            _connectToEsp32(station.ipAddress, station.port, station);
                          }
                        },
                      ),
                    ],
                  ),
                ]),
                Text("Range: ${station.rangeData}"),
                Text("Force: ${station.forceData}"),
              ],
            );
          }).toList(),
        ),
      ),
    );
  }
}
wpx232ag

wpx232ag1#

TabController的重新构建(或者更确切地说是改变它的状态)有点棘手,所以解决方案之一是在需要重新构建它(它的状态)时更新它的键。下面是一个完整的工作示例,说明如何以编程方式添加一个新选项卡,以便您可以根据需要调整它:

class Sample extends StatefulWidget {
  const Sample({super.key});

  @override
  State<Sample> createState() => _SampleState();
}

class _SampleState extends State<Sample> {
  final List<Tab> tabs = [Tab(text: "Tab 1"), Tab(text: "Tab 2")];
  var tabControllerKey = GlobalKey();

  void _openAddTabDialog() {
    final tabNameController = TextEditingController();
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Add Tab'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextFormField(
                controller: tabNameController,
                decoration: const InputDecoration(labelText: 'Tab Name'),
              ),
            ],
          ),
          actions: [
            ElevatedButton.icon(
              onPressed: () {
                setState(() {
                  tabs.add(Tab(text: tabNameController.text));
                  // this is needed for the tab controller to update
                  tabControllerKey = GlobalKey();
                });
                Navigator.pop(context);
              },
              icon: const Icon(Icons.add),
              label: const Text('Add'),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      key: tabControllerKey,
      length: tabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Dynamic Tabs'),
          actions: [
            ElevatedButton.icon(
              onPressed: _openAddTabDialog,
              icon: const Icon(Icons.add),
              label: const Text('Add Tab'),
            ),
          ],
          bottom: TabBar(tabs: tabs),
        ),
        body: SafeArea(
          child: TabBarView(children: tabs),
        ),
      ),
    );
  }
}

相关问题