Преглед изворни кода

WW-weekly progress update:
feat: Add Existing Member Hidden Detail screen and Messages page

- Created ExistingMemberHiddenDetail screen with image carousel, product info, and description.
- Implemented MessagesPage with tabbed navigation for different message categories.
- Added WebSocket functionality with connection and message sending capabilities.
- Updated UserInterfaceList to include new screens for navigation.
- Integrated file_selector plugin for file selection capabilities on Linux and Windows.
- Updated pubspec.yaml to include necessary dependencies for new features.
- Adjusted platform-specific generated files for plugin registration.

Dave Lim пре 3 недеља
родитељ
комит
160b943210

+ 1 - 0
bloc_example/ios/Flutter/Debug.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
 #include "Generated.xcconfig"

+ 1 - 0
bloc_example/ios/Flutter/Release.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
 #include "Generated.xcconfig"

+ 44 - 0
bloc_example/ios/Podfile

@@ -0,0 +1,44 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '12.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+  'Debug' => :debug,
+  'Profile' => :release,
+  'Release' => :release,
+}
+
+def flutter_root
+  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+  unless File.exist?(generated_xcode_build_settings_path)
+    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+  end
+
+  File.foreach(generated_xcode_build_settings_path) do |line|
+    matches = line.match(/FLUTTER_ROOT\=(.*)/)
+    return matches[1].strip if matches
+  end
+  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+  use_frameworks!
+  use_modular_headers!
+
+  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+  target 'RunnerTests' do
+    inherit! :search_paths
+  end
+end
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    flutter_additional_ios_build_settings(target)
+  end
+end

+ 245 - 0
bloc_example/lib/screens/chat_room.dart

@@ -0,0 +1,245 @@
+import 'package:flutter/material.dart';
+
+class ChatRoom extends StatelessWidget {
+  final List<Map<String, dynamic>> messages = [
+    {
+      'sender': 'system',
+      'text': '尊敬的经纪:您好,欢迎来到《名媛汇》高端外围服务平台。您可以通过下方的介绍简单的了解一下平台介绍、客服服务、交易流程、常见问题',
+      'time': '14:20'
+    },
+    {'sender': 'system', 'text': '商务客服24小时在线,如有疑问请及时沟通', 'time': '14:20'},
+    {
+      'sender': 'user',
+      'text': 'Proin gravida dolor sit amet lacus accumsan et viverra.',
+      'time': '14:22'
+    },
+    {
+      'sender': 'system',
+      'text': '订单号:100000001,尾款已交收,请及时退款,谢谢配合!',
+      'time': '14:21'
+    },
+    {
+      'sender': 'system',
+      'text': '订单号:100000001,尾款已交收,请及时退款,谢谢配合!',
+      'time': '14:21'
+    },
+    {
+      'sender': 'system',
+      'text': '订单号:100000001,尾款已交收,请及时退款,谢谢配合!',
+      'time': '14:21'
+    },
+    {
+      'sender': 'system',
+      'text': '订单号:100000001,尾款已交收,请及时退款,谢谢配合!',
+      'time': '14:21'
+    },
+    {
+      'sender': 'system',
+      'text': '订单号:100000001,尾款已交收,请及时退款,谢谢配合!',
+      'time': '14:21'
+    },
+    {
+      'sender': 'system',
+      'text': '订单号:100000001,尾款已交收,请及时退款,谢谢配合!',
+      'time': '14:22'
+    },
+  ];
+
+  final List<Map<String, String>> avatars = [
+    {
+      'name': '北京XX',
+      'image': 'https://picsum.photos/80/100',
+    },
+    {
+      'name': '上海XX',
+      'image': 'https://picsum.photos/80/100',
+    },
+    {
+      'name': '校花XX',
+      'image': 'https://picsum.photos/80/100',
+    },
+    {
+      'name': '嫩模XX',
+      'image': 'https://picsum.photos/80/100',
+    },
+    {
+      'name': '空姐XX',
+      'image': 'https://picsum.photos/80/100',
+    },
+  ];
+
+  final List<String> quickReplies = ['名媛汇', '交易流程', '常见问题'];
+  final List<String> paymentReplies = ['支付回款', '订单群'];
+
+  ChatRoom({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: PreferredSize(
+        preferredSize: Size(double.infinity, 64),
+        child: AppBar(
+            centerTitle: true,
+            title: const Column(children: <Widget>[
+              Text('选妃助手'),
+              Row(
+                mainAxisAlignment: MainAxisAlignment.center,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: [
+                  Icon(Icons.circle, color: Colors.blue, size: 10),
+                  SizedBox(width: 4),
+                  Text("机器人",
+                      style: TextStyle(fontSize: 14, color: Colors.blue)),
+                ],
+              )
+            ])),
+      ),
+      // AppBar(
+      //   leading: const BackButton(),
+      //   title: const Text('选妃助手'),
+      //   centerTitle: true,
+      //   bottom: const PreferredSize(
+      //     preferredSize: const Size.fromHeight(32),
+      //     child: Column(
+      //       children: [
+      //         const Text(
+      //           '机器人',
+      //           style: TextStyle(color: Colors.blue, fontSize: 14),
+      //         ),
+      //         const SizedBox(height: 8),
+      //       ],
+      //     ),
+      //   ),
+      // ),
+      body: Column(
+        children: [
+          SingleChildScrollView(
+            scrollDirection: Axis.horizontal,
+            padding: const EdgeInsets.symmetric(horizontal: 12),
+            child: Row(
+              children: avatars.map((item) {
+                return Padding(
+                  padding: const EdgeInsets.symmetric(horizontal: 6.0),
+                  child: Column(
+                    children: [
+                      ClipRRect(
+                        borderRadius: BorderRadius.circular(8),
+                        child: Image.network(
+                          item['image']!,
+                          width: 80,
+                          height: 100,
+                          fit: BoxFit.cover,
+                        ),
+                      ),
+                      Container(
+                        width: 80,
+                        alignment: Alignment.center,
+                        padding: const EdgeInsets.symmetric(vertical: 4),
+                        decoration: BoxDecoration(
+                          color: Colors.black54,
+                          borderRadius:
+                              BorderRadius.vertical(bottom: Radius.circular(8)),
+                        ),
+                        child: Text(
+                          item['name']!,
+                          style: const TextStyle(
+                              color: Colors.white, fontSize: 14),
+                        ),
+                      )
+                    ],
+                  ),
+                );
+              }).toList(),
+            ),
+          ),
+
+          Expanded(
+            child: ListView.builder(
+              padding: const EdgeInsets.all(16),
+              itemCount: messages.length,
+              itemBuilder: (context, index) {
+                final msg = messages[index];
+                final isUser = msg['sender'] == 'user';
+                return Align(
+                  alignment:
+                      isUser ? Alignment.centerRight : Alignment.centerLeft,
+                  child: Container(
+                    padding: const EdgeInsets.all(12),
+                    margin: const EdgeInsets.symmetric(vertical: 4),
+                    constraints: const BoxConstraints(maxWidth: 300),
+                    decoration: BoxDecoration(
+                      color: isUser ? Colors.lightBlueAccent : Colors.grey[200],
+                      borderRadius: BorderRadius.circular(12),
+                    ),
+                    child: Column(
+                      crossAxisAlignment: isUser
+                          ? CrossAxisAlignment.end
+                          : CrossAxisAlignment.start,
+                      children: [
+                        Text(
+                          msg['text'],
+                          style: const TextStyle(fontSize: 14),
+                        ),
+                        const SizedBox(height: 4),
+                        Text(
+                          msg['time'],
+                          style: const TextStyle(
+                              fontSize: 10, color: Colors.black45),
+                        ),
+                      ],
+                    ),
+                  ),
+                );
+              },
+            ),
+          ),
+
+          // Quick Reply Section
+          // Padding(
+          //   padding: const EdgeInsets.symmetric(horizontal: 8),
+          //   child: Wrap(
+          //     spacing: 8,
+          //     runSpacing: 8,
+          //     children: quickReplies
+          //         .map((reply) => OutlinedButton(
+          //               onPressed: () {},
+          //               child: Text(reply),
+          //             ))
+          //         .toList(),
+          //   ),
+          // ),
+          // const SizedBox(height: 8),
+
+          // Padding(
+          //   padding: const EdgeInsets.symmetric(horizontal: 8),
+          //   child: Wrap(
+          //     spacing: 8,
+          //     runSpacing: 8,
+          //     children: paymentReplies
+          //         .map((reply) => OutlinedButton(
+          //               onPressed: () {},
+          //               child: Text(reply),
+          //             ))
+          //         .toList(),
+          //   ),
+          // ),
+
+          // Bottom sticky button
+          Container(
+            width: double.infinity,
+            margin: const EdgeInsets.all(16),
+            child: ElevatedButton(
+              style: ElevatedButton.styleFrom(
+                backgroundColor: Colors.lightBlue,
+                padding: const EdgeInsets.symmetric(vertical: 16),
+              ),
+              onPressed: () {},
+              child: const Text('开始服务',
+                  style: TextStyle(fontSize: 16, color: Colors.white)),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 561 - 0
bloc_example/lib/screens/escort_publish.dart

@@ -0,0 +1,561 @@
+import 'package:flutter/material.dart';
+import 'dart:io';
+import 'package:image_picker/image_picker.dart';
+
+class EscortPublish extends StatefulWidget {
+  @override
+  State<EscortPublish> createState() => _EscortPublishState();
+}
+
+class _EscortPublishState extends State<EscortPublish> {
+  final _nameController = TextEditingController();
+  final _phoneController = TextEditingController();
+  final _addressController = TextEditingController();
+
+  final List<String> levels = ['平价', '中端', '高端', '顶级'];
+  final List<String> services = ['工作室', '上门', '空降', '伴游'];
+  final List<String> taboos = ['DU品', '吃YAO', 'WU套', '醉9'];
+  final List<String> tags = [
+    '学生',
+    '良家',
+    'OL',
+    '网红',
+    '模特',
+    '明星',
+    '新人',
+    '兼职',
+    '短期',
+    '原装',
+    'S身材',
+    '高颜值',
+    '三点粉',
+    '微胖',
+    '水嫩',
+    '紧致',
+    '清纯',
+    '轻熟',
+    '气质',
+    '纯欲'
+  ];
+  final List<String> packages = [
+    '双飞',
+    '3P',
+    '口爆',
+    '胸推',
+    '足交',
+    '毒龙',
+    '做爱',
+    '69',
+    '无套内射',
+    '舌吻',
+    '水床',
+    '制服',
+    '冰火',
+    '环游',
+    '调教',
+    '喷水',
+    '洗澡',
+    '剧情',
+    'SM',
+    '三通'
+  ];
+  String? selectedLevel;
+
+  String? selectedCity;
+  String? selectedYear;
+  String? selectedWeight;
+  String? selectedHeight;
+  String? selectedCup;
+
+  List<String> selectedServices = [];
+  List<String> selectedTaboos = [];
+  List<String> selectedTags = [];
+
+  List<String> selectedPackages = [];
+  List<XFile> imageFiles = [];
+  XFile? videoFile;
+  bool hideFromPublic = false;
+  final TextEditingController _descController = TextEditingController();
+  final ImagePicker _picker = ImagePicker();
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text.rich(
+          TextSpan(
+            text: '发布外围',
+            children: [
+              TextSpan(
+                text: '(请保证妹子信息准确)',
+                style: TextStyle(fontSize: 14, color: Colors.grey),
+              )
+            ],
+          ),
+        ),
+        leading: const BackButton(),
+      ),
+      body: SingleChildScrollView(
+        padding: const EdgeInsets.all(16),
+        child: Column(
+          children: [
+            buildTextField("外围花名", "(必填)", "请输入外围花名,不超过10个字", _nameController),
+            buildDropdownField("所在城市", "(必选)", selectedCity, ['北京', '上海', '广州'],
+                (val) {
+              setState(() => selectedCity = val);
+            }),
+            buildTextField("手机号码", "(必填,不会自动发送给用户)", "请输入妹子联系方式,不超过50个字",
+                _phoneController),
+            buildTextField("详细地址", "(必填,不会自动发送给用户)", "请输入妹子工作室地址,不超过50个字",
+                _addressController),
+            buildDropdownField("出生年份", "(必选)", selectedYear,
+                List.generate(25, (i) => '${2000 - i}'), (val) {
+              setState(() => selectedYear = val);
+            }),
+            buildDropdownField(
+                "体重", "(必选)", selectedWeight, ['40kg', '45kg', '50kg', '55kg'],
+                (val) {
+              setState(() => selectedWeight = val);
+            }),
+            buildDropdownField(
+                "身高", "(必选)", selectedHeight, ['150cm', '160cm', '170cm'],
+                (val) {
+              setState(() => selectedHeight = val);
+            }),
+            buildDropdownField(
+                "罩杯", "(必选)", selectedCup, ['A', 'B', 'C', 'D', 'E'], (val) {
+              setState(() => selectedCup = val);
+            }),
+            const SizedBox(height: 20),
+            Align(
+              alignment: Alignment.centerLeft,
+              child: RichText(
+                text: const TextSpan(
+                  style: TextStyle(fontSize: 16, color: Colors.black),
+                  children: [
+                    TextSpan(text: '请选择妹子档次'),
+                    TextSpan(text: '(单选)', style: TextStyle(color: Colors.red)),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 10),
+            Wrap(
+              spacing: 12,
+              children: levels.map((level) {
+                final selected = level == selectedLevel;
+                return ChoiceChip(
+                  label: Text(level),
+                  selected: selected,
+                  onSelected: (_) {
+                    setState(() => selectedLevel = level);
+                  },
+                  selectedColor: Colors.blue,
+                  labelStyle:
+                      TextStyle(color: selected ? Colors.white : Colors.black),
+                );
+              }).toList(),
+            ),
+            const SizedBox(height: 20),
+            Align(
+              alignment: Alignment.centerLeft,
+              child: RichText(
+                text: const TextSpan(
+                  style: TextStyle(fontSize: 16, color: Colors.black),
+                  children: [
+                    TextSpan(text: '服务套餐'),
+                    TextSpan(
+                        text: '(最少选填1项)', style: TextStyle(color: Colors.red)),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 10),
+            buildSelectableChips(
+              options: services,
+              selectedList: selectedServices,
+              maxSelect: services.length,
+              isSingle: false,
+              onTap: (_) {},
+            ),
+            const SizedBox(height: 12),
+            buildPackageInputFields(),
+            const SizedBox(height: 20),
+            Align(
+              alignment: Alignment.centerLeft,
+              child: RichText(
+                text: const TextSpan(
+                  style: TextStyle(fontSize: 16, color: Colors.black),
+                  children: [
+                    TextSpan(text: '禁忌'),
+                    TextSpan(text: '(多选)', style: TextStyle(color: Colors.red)),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 10),
+            buildSelectableChips(
+              options: taboos,
+              selectedList: selectedTaboos,
+              maxSelect: taboos.length,
+              isSingle: false,
+              onTap: (_) {},
+            ),
+            const SizedBox(height: 20),
+            Align(
+              alignment: Alignment.centerLeft,
+              child: RichText(
+                text: const TextSpan(
+                  style: TextStyle(fontSize: 16, color: Colors.black),
+                  children: [
+                    TextSpan(text: '外圈标签'),
+                    TextSpan(
+                        text: '(必选,最多3个)', style: TextStyle(color: Colors.red)),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 10),
+            buildSelectableChips(
+              options: tags,
+              selectedList: selectedTags,
+              maxSelect: 3,
+              isSingle: false,
+              onTap: (_) {},
+            ),
+            Align(
+              alignment: Alignment.centerLeft,
+              child: RichText(
+                text: const TextSpan(
+                  style: TextStyle(fontSize: 16, color: Colors.black),
+                  children: [
+                    TextSpan(text: '服务项目'),
+                    TextSpan(
+                        text: '(必选,最多20个)',
+                        style: TextStyle(color: Colors.red)),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 10),
+            buildSelectableChips(
+              options: packages,
+              selectedList: selectedPackages,
+              maxSelect: 20,
+              isSingle: false,
+              onTap: (_) {},
+            ),
+            const SizedBox(height: 20),
+            Align(
+              alignment: Alignment.centerLeft,
+              child: RichText(
+                text: const TextSpan(
+                  style: TextStyle(fontSize: 16, color: Colors.black),
+                  children: [
+                    TextSpan(text: '外圈介绍'),
+                    TextSpan(text: '(选填)', style: TextStyle(color: Colors.red)),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 10),
+            TextField(
+              controller: _descController,
+              maxLength: 200,
+              maxLines: 4,
+              decoration: const InputDecoration(
+                hintText: '请输入外圈介绍,不超过200字',
+                border: OutlineInputBorder(),
+              ),
+            ),
+            const SizedBox(height: 20),
+            Align(
+              alignment: Alignment.centerLeft,
+              child: RichText(
+                text: const TextSpan(
+                  style: TextStyle(fontSize: 16, color: Colors.black),
+                  children: [
+                    TextSpan(text: '上传图片'),
+                    TextSpan(
+                        text: '(最少1张,最多9张)',
+                        style: TextStyle(color: Colors.red)),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 10),
+            buildImageUploader(),
+            const SizedBox(height: 20),
+            Row(
+              children: [
+                Align(
+                  alignment: Alignment.centerLeft,
+                  child: RichText(
+                    text: const TextSpan(
+                      style: TextStyle(fontSize: 16, color: Colors.black),
+                      children: [
+                        TextSpan(text: '官方认证视频'),
+                        TextSpan(
+                            text: '(选填,不超过100M)',
+                            style: TextStyle(color: Colors.red)),
+                      ],
+                    ),
+                  ),
+                ),
+                Spacer(),
+                Row(
+                  children: [
+                    Radio<bool>(
+                      value: true,
+                      groupValue: hideFromPublic,
+                      onChanged: (_) => setState(() => hideFromPublic = true),
+                    ),
+                    const Text("不对外展示", style: TextStyle(fontSize: 12)),
+                  ],
+                ),
+              ],
+            ),
+            const SizedBox(height: 8),
+            GestureDetector(
+              onTap: pickVideo,
+              child: Container(
+                height: 160,
+                color: Colors.grey[100],
+                child: Center(
+                  child: videoFile == null
+                      ? const Icon(Icons.videocam, size: 40, color: Colors.grey)
+                      : const Icon(Icons.check_circle,
+                          color: Colors.green, size: 40),
+                ),
+              ),
+            ),
+            const SizedBox(height: 8),
+            const Text("① 视频需包含本人露脸、当前日期、所在城市/名媛会",
+                style: TextStyle(color: Colors.grey)),
+            const Text("例如:3月3日,名媛会露脸在苏州等你",
+                style: TextStyle(color: Colors.grey)),
+            const Text("② 审核通过率高,通过后可获得官方认证标识",
+                style: TextStyle(color: Colors.grey)),
+            const Text("③ 选择对外展示可额外获得官方流量扶持+佣金优惠",
+                style: TextStyle(color: Colors.grey)),
+            const SizedBox(height: 30),
+            SizedBox(
+              width: double.infinity,
+              child: ElevatedButton(
+                onPressed: () {},
+                style: ElevatedButton.styleFrom(
+                  padding: const EdgeInsets.symmetric(vertical: 16),
+                  shape: RoundedRectangleBorder(
+                      borderRadius: BorderRadius.circular(6)),
+                  backgroundColor: Colors.blue,
+                ),
+                child: const Text("提交", style: TextStyle(fontSize: 16)),
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget buildTextField(String label, String hint, String placeholder,
+      TextEditingController controller) {
+    return Padding(
+      padding: const EdgeInsets.symmetric(vertical: 8),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          buildLabel(label, hint),
+          const SizedBox(height: 6),
+          TextField(
+            controller: controller,
+            decoration: InputDecoration(
+              hintText: placeholder,
+              border: OutlineInputBorder(),
+              contentPadding:
+                  const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget buildDropdownField(String label, String hint, String? value,
+      List<String> options, ValueChanged<String?> onChanged) {
+    return Padding(
+      padding: const EdgeInsets.symmetric(vertical: 8),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          buildLabel(label, hint),
+          const SizedBox(height: 6),
+          DropdownButtonFormField<String>(
+            value: value,
+            items: options
+                .map((e) => DropdownMenuItem(value: e, child: Text(e)))
+                .toList(),
+            onChanged: onChanged,
+            decoration: InputDecoration(
+              border: OutlineInputBorder(),
+              contentPadding:
+                  const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget buildLabel(String title, String hint) {
+    return RichText(
+      text: TextSpan(
+        text: title,
+        style: const TextStyle(fontSize: 16, color: Colors.black),
+        children: [
+          TextSpan(text: ' $hint', style: const TextStyle(color: Colors.red)),
+        ],
+      ),
+    );
+  }
+
+  Widget buildSelectableChips({
+    required List<String> options,
+    required List<String> selectedList,
+    required int maxSelect,
+    required bool isSingle,
+    required Function(String) onTap,
+  }) {
+    return Wrap(
+      spacing: 8,
+      runSpacing: 8,
+      children: options.map((option) {
+        final bool selected = selectedList.contains(option);
+        return ChoiceChip(
+          label: Text(option),
+          selected: selected,
+          onSelected: (_) {
+            setState(() {
+              if (isSingle) {
+                selectedList
+                  ..clear()
+                  ..add(option);
+              } else {
+                if (selected) {
+                  selectedList.remove(option);
+                } else if (selectedList.length < maxSelect) {
+                  selectedList.add(option);
+                }
+              }
+              onTap(option);
+            });
+          },
+        );
+      }).toList(),
+    );
+  }
+
+  Widget buildPackageInputFields() {
+    return Column(
+      children: List.generate(4, (i) {
+        return Row(
+          children: [
+            Expanded(
+              child: TextField(
+                decoration: InputDecoration(
+                  hintText: '请输入服务套餐${i + 1}${i == 0 ? ',必填' : ''}',
+                  border: OutlineInputBorder(),
+                  contentPadding:
+                      const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
+                ),
+              ),
+            ),
+            const SizedBox(width: 8),
+            Expanded(
+              child: TextField(
+                decoration: InputDecoration(
+                  hintText: '请输入套餐${i + 1}价格${i == 0 ? ',必填' : ''}',
+                  border: OutlineInputBorder(),
+                  contentPadding:
+                      const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
+                ),
+                keyboardType: TextInputType.number,
+              ),
+            ),
+          ],
+        );
+      }),
+    );
+  }
+
+  Widget buildImageUploader() {
+    return Wrap(
+      spacing: 8,
+      runSpacing: 8,
+      children: List.generate(9, (index) {
+        if (index < imageFiles.length) {
+          return Stack(
+            children: [
+              Image.file(
+                File(imageFiles[index].path),
+                width: 100,
+                height: 100,
+                fit: BoxFit.cover,
+              ),
+              Positioned(
+                top: 0,
+                right: 0,
+                child: GestureDetector(
+                  onTap: () {
+                    setState(() {
+                      imageFiles.removeAt(index);
+                    });
+                  },
+                  child: const Icon(Icons.cancel, color: Colors.red, size: 20),
+                ),
+              ),
+            ],
+          );
+        } else if (index == imageFiles.length) {
+          return GestureDetector(
+            onTap: pickImages,
+            child: Container(
+              width: 100,
+              height: 100,
+              color: Colors.grey[200],
+              child: const Icon(Icons.add),
+            ),
+          );
+        } else {
+          return Container(
+            width: 100,
+            height: 100,
+            color: Colors.grey[100],
+          );
+        }
+      }),
+    );
+  }
+
+  Future<void> pickVideo() async {
+    final XFile? picked = await _picker.pickVideo(
+      source: ImageSource.gallery,
+      maxDuration: const Duration(minutes: 5),
+    );
+    if (picked != null) {
+      setState(() {
+        videoFile = picked;
+      });
+    }
+  }
+
+  Future<void> pickImages() async {
+    final List<XFile>? images = await _picker.pickMultiImage();
+    if (images != null && images.isNotEmpty) {
+      setState(() {
+        final remainingSlots = 9 - imageFiles.length;
+        imageFiles.addAll(images.take(remainingSlots));
+      });
+    }
+  }
+}

+ 2 - 4
bloc_example/lib/screens/existing_member_hidden.dart

@@ -8,7 +8,6 @@ class ExistingMemberHidden extends StatefulWidget {
 }
 
 class _ExistingMemberHiddenState extends State<ExistingMemberHidden> {
-  // Sample data for tea items
   final List<Map<String, dynamic>> teaItems = [
     {
       'type': '红茶',
@@ -50,16 +49,15 @@ class _ExistingMemberHiddenState extends State<ExistingMemberHidden> {
       'name': '平阳黄汤',
       'price': '¥ 388/斤',
       'sales': '110人购买',
-      'color': Colors.yellow[800], // Using a dark yellow for visibility
+      'color': Colors.yellow[800],
     },
-    // Add more items as needed
   ];
 
   @override
   Widget build(BuildContext context) {
     return Scaffold(
       appBar: AppBar(
-        title: const Text('茶叶详情'), // Example AppBar title
+        title: const Text('茶叶详情'),
       ),
       body: SingleChildScrollView(
         child: Column(

+ 155 - 0
bloc_example/lib/screens/existing_member_hidden_detail.dart

@@ -0,0 +1,155 @@
+import 'package:flutter/material.dart';
+
+class ExistingMemberHiddenDetail extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('茶叶详情'),
+        leading: IconButton(
+          icon: const Icon(Icons.arrow_back),
+          onPressed: () => Navigator.pop(context),
+        ),
+      ),
+      body: Stack(
+        children: [
+          Padding(
+            padding: const EdgeInsets.only(bottom: 70.0),
+            child: SingleChildScrollView(
+              child: Column(
+                children: [
+                  // Image carousel placeholder
+                  Container(
+                    color: Colors.grey[400],
+                    height: 200,
+                    width: double.infinity,
+                    child: const Center(
+                      child: Column(
+                        mainAxisAlignment: MainAxisAlignment.center,
+                        children: [
+                          Icon(Icons.image, size: 60, color: Colors.white),
+                          SizedBox(height: 8),
+                          Text("图片加载中...",
+                              style: TextStyle(color: Colors.white)),
+                        ],
+                      ),
+                    ),
+                  ),
+
+                  const SizedBox(height: 12),
+
+                  // Product info
+                  Container(
+                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
+                    width: double.infinity,
+                    child: const Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Text(
+                          '杭州龙井',
+                          style: TextStyle(
+                              fontSize: 20, fontWeight: FontWeight.bold),
+                        ),
+                        SizedBox(height: 4),
+                        Text('99人购买 | 累计销售 8950 克'),
+                        SizedBox(height: 4),
+                        Text(
+                          '¥999  (50克)',
+                          style: TextStyle(fontSize: 16, color: Colors.red),
+                        ),
+                      ],
+                    ),
+                  ),
+
+                  const SizedBox(height: 12),
+
+                  // Description section
+                  const Padding(
+                    padding: EdgeInsets.symmetric(horizontal: 16.0),
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Divider(),
+                        Text(
+                          '茶叶介绍',
+                          style: TextStyle(
+                              fontSize: 16, fontWeight: FontWeight.bold),
+                        ),
+                        SizedBox(height: 8),
+                        Text(
+                          '介绍一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
+                          '一二三四五六七八九十一二三四五六七八九十一',
+                          style: TextStyle(fontSize: 14),
+                        ),
+                      ],
+                    ),
+                  ),
+
+                  const SizedBox(height: 12),
+                ],
+              ),
+            ),
+          ),
+          // Bottom bar
+          Positioned(
+            bottom: 0,
+            left: 0,
+            right: 0,
+            height: 70,
+            child: Container(
+              color: Colors.grey[100],
+              padding:
+                  const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12),
+              child: Row(
+                children: [
+                  const Expanded(
+                    child: Text(
+                      '联系方式:18888888888',
+                      style: TextStyle(fontSize: 16),
+                    ),
+                  ),
+                  ElevatedButton.icon(
+                    onPressed: () {
+                      // Add call or message logic
+                    },
+                    style: ElevatedButton.styleFrom(
+                      backgroundColor: Colors.pinkAccent,
+                      padding: const EdgeInsets.symmetric(
+                          horizontal: 16, vertical: 12),
+                    ),
+                    icon: const Icon(
+                      Icons.phone,
+                      color: Colors.white,
+                    ),
+                    label: const Text(
+                      '联系购买',
+                      style: TextStyle(color: Colors.white),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 150 - 0
bloc_example/lib/screens/messages_page.dart

@@ -0,0 +1,150 @@
+import 'package:flutter/material.dart';
+
+class MessagesPage extends StatefulWidget {
+  @override
+  State<MessagesPage> createState() => _MessagesPageState();
+}
+
+class _MessagesPageState extends State<MessagesPage>
+    with SingleTickerProviderStateMixin {
+  late TabController _tabController;
+
+  final List<String> tabs = ['全部', '客服', '待付尾款', '待付回款', '订单完成', '订单撤销'];
+
+  @override
+  void initState() {
+    _tabController = TabController(length: tabs.length, vsync: this);
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    _tabController.dispose();
+    super.dispose();
+  }
+
+  Widget _buildMessageItem({
+    required String title,
+    required String subtitle,
+    required String leading,
+    required String time,
+    int? badgeCount,
+  }) {
+    return ListTile(
+      leading: CircleAvatar(
+        backgroundColor: Colors.grey.shade200,
+        child: Text(leading, style: const TextStyle(color: Colors.black)),
+      ),
+      title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
+      subtitle: Text(subtitle),
+      trailing: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Text(time, style: const TextStyle(color: Colors.grey)),
+          if (badgeCount != null)
+            Container(
+              margin: const EdgeInsets.only(top: 4),
+              padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
+              decoration: BoxDecoration(
+                color: badgeCount > 0 ? Colors.red : Colors.grey,
+                borderRadius: BorderRadius.circular(12),
+              ),
+              child: Text(
+                badgeCount > 99 ? '99+' : '$badgeCount',
+                style: const TextStyle(color: Colors.white, fontSize: 12),
+              ),
+            ),
+        ],
+      ),
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('消息', style: TextStyle(fontWeight: FontWeight.bold)),
+        centerTitle: true,
+        actions: [
+          IconButton(onPressed: () {}, icon: const Icon(Icons.search)),
+        ],
+        bottom: TabBar(
+          controller: _tabController,
+          isScrollable: true,
+          labelColor: Colors.blue,
+          unselectedLabelColor: Colors.black,
+          indicatorColor: Colors.blue,
+          tabs: tabs.map((tab) => Tab(text: tab)).toList(),
+        ),
+      ),
+      body: TabBarView(
+        controller: _tabController,
+        children: tabs.map((_) {
+          return ListView(
+            children: [
+              _buildMessageItem(
+                leading: '系统',
+                title: '[置顶]系统消息',
+                subtitle: '您的订单已完成',
+                time: '3月3日',
+              ),
+              _buildMessageItem(
+                leading: '官方',
+                title: '[置顶]商务客服',
+                subtitle: '[客服] 欢迎来到名媛汇',
+                time: '24/3/3',
+                badgeCount: 6,
+              ),
+              _buildMessageItem(
+                leading: '订单',
+                title: '3/3 XXX-XXX-XXX',
+                subtitle: '[待付尾款]',
+                time: '02:02',
+                badgeCount: 999,
+              ),
+              _buildMessageItem(
+                leading: '订单',
+                title: '3/3 XXX-XXX-XXX',
+                subtitle: '[待付回款]',
+                time: '周三',
+                badgeCount: 99,
+              ),
+              _buildMessageItem(
+                leading: '订单',
+                title: '3/3 XXX-XXX-XXX',
+                subtitle: '[订单完成]',
+                time: '24/3/3',
+              ),
+              _buildMessageItem(
+                leading: '订单',
+                title: '3/3 XXX-XXX-XXX',
+                subtitle: '[订单撤销]',
+                time: '24/3/3',
+              ),
+              _buildMessageItem(
+                leading: '订单',
+                title: '3/3 XXX-XXX-XXX',
+                subtitle: '[待付尾款]',
+                time: '24/3/3',
+                badgeCount: 10,
+              ),
+              _buildMessageItem(
+                leading: '订单',
+                title: '3/3 XXX-XXX-XXX',
+                subtitle: '[待付回款]',
+                time: '24/3/3',
+              ),
+              _buildMessageItem(
+                leading: '订单',
+                title: '3/3 XXX-XXX-XXX',
+                subtitle: '[待付尾款]',
+                time: '24/3/3',
+                badgeCount: 9,
+              ),
+            ],
+          );
+        }).toList(),
+      ),
+    );
+  }
+}

+ 10 - 0
bloc_example/lib/screens/user_interface_list.dart

@@ -1,3 +1,8 @@
+import 'package:bloc_example/screens/chat_room.dart';
+import 'package:bloc_example/screens/escort_publish.dart';
+import 'package:bloc_example/screens/existing_member_hidden_detail.dart';
+import 'package:bloc_example/screens/messages_page.dart';
+import 'package:bloc_example/screens/web_socket.dart';
 import 'package:flutter/material.dart';
 import 'pin_input_screen.dart';
 import 'new_member_without_channel.dart';
@@ -10,6 +15,11 @@ class UserInterfaceList extends StatelessWidget {
     _ScreenItem('New Member Without Channel', NewMemberWithoutChannel()),
     _ScreenItem('New Member With Channel', NewMemberWithChannel()),
     _ScreenItem('Existing Member Hidden', ExistingMemberHidden()),
+    _ScreenItem('Existing Member Hidden Detail', ExistingMemberHiddenDetail()),
+    _ScreenItem('Chat Room', ChatRoom()),
+    _ScreenItem('Messages', MessagesPage()),
+    _ScreenItem('Escort Publish', EscortPublish()),
+    _ScreenItem('Web Socket', WebSocketScreen()),
   ];
 
   @override

+ 60 - 0
bloc_example/lib/screens/web_socket.dart

@@ -0,0 +1,60 @@
+import 'package:web_socket_channel/web_socket_channel.dart';
+import 'package:web_socket_channel/status.dart' as status;
+
+import 'package:flutter/material.dart';
+
+class WebSocketScreen extends StatelessWidget {
+  const WebSocketScreen({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('WebSocket Example'),
+        leading: IconButton(
+          icon: const Icon(Icons.arrow_back),
+          onPressed: () => Navigator.pop(context),
+        ),
+      ),
+      body: Center(
+        child: Column(
+          children: [
+            ElevatedButton(
+              onPressed: () async {
+                connectButtonClicked();
+              },
+              child: const Text('Connect to WebSocket'),
+            ),
+            ElevatedButton(
+              onPressed: () async {
+                sendButtonClicked();
+              },
+              child: const Text('Send Message'),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+void connectButtonClicked() async {
+  final wsUrl = Uri.parse('ws://47.76.151.238:82/ws');
+  final channel = WebSocketChannel.connect(wsUrl);
+
+  await channel.ready;
+
+  channel.stream.listen((message) {
+    print('Received: $message');
+  });
+
+  await Future.delayed(Duration(seconds: 2));
+  channel.sink.add('Hello World!');
+  print('Sent: Hello World!');
+
+  await Future.delayed(Duration(seconds: 2));
+  channel.sink.close(status.goingAway);
+  print('Connection closed');
+}
+
+void sendButtonClicked() async {}

+ 4 - 0
bloc_example/linux/flutter/generated_plugin_registrant.cc

@@ -6,6 +6,10 @@
 
 #include "generated_plugin_registrant.h"
 
+#include <file_selector_linux/file_selector_plugin.h>
 
 void fl_register_plugins(FlPluginRegistry* registry) {
+  g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
+  file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
 }

+ 1 - 0
bloc_example/linux/flutter/generated_plugins.cmake

@@ -3,6 +3,7 @@
 #
 
 list(APPEND FLUTTER_PLUGIN_LIST
+  file_selector_linux
 )
 
 list(APPEND FLUTTER_FFI_PLUGIN_LIST

+ 1 - 0
bloc_example/macos/Flutter/Flutter-Debug.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
 #include "ephemeral/Flutter-Generated.xcconfig"

+ 1 - 0
bloc_example/macos/Flutter/Flutter-Release.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
 #include "ephemeral/Flutter-Generated.xcconfig"

+ 2 - 0
bloc_example/macos/Flutter/GeneratedPluginRegistrant.swift

@@ -5,6 +5,8 @@
 import FlutterMacOS
 import Foundation
 
+import file_selector_macos
 
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+  FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
 }

+ 43 - 0
bloc_example/macos/Podfile

@@ -0,0 +1,43 @@
+platform :osx, '10.14'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+  'Debug' => :debug,
+  'Profile' => :release,
+  'Release' => :release,
+}
+
+def flutter_root
+  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+  unless File.exist?(generated_xcode_build_settings_path)
+    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+  end
+
+  File.foreach(generated_xcode_build_settings_path) do |line|
+    matches = line.match(/FLUTTER_ROOT\=(.*)/)
+    return matches[1].strip if matches
+  end
+  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+  use_frameworks!
+  use_modular_headers!
+
+  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+  target 'RunnerTests' do
+    inherit! :search_paths
+  end
+end
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    flutter_additional_macos_build_settings(target)
+  end
+end

+ 190 - 1
bloc_example/pubspec.lock

@@ -49,6 +49,22 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.18.0"
+  cross_file:
+    dependency: transitive
+    description:
+      name: cross_file
+      sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.3.4+2"
+  crypto:
+    dependency: transitive
+    description:
+      name: crypto
+      sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.6"
   cupertino_icons:
     dependency: "direct main"
     description:
@@ -65,6 +81,38 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.3.1"
+  file_selector_linux:
+    dependency: transitive
+    description:
+      name: file_selector_linux
+      sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.9.3+2"
+  file_selector_macos:
+    dependency: transitive
+    description:
+      name: file_selector_macos
+      sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.9.4+2"
+  file_selector_platform_interface:
+    dependency: transitive
+    description:
+      name: file_selector_platform_interface
+      sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.6.2"
+  file_selector_windows:
+    dependency: transitive
+    description:
+      name: file_selector_windows
+      sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.9.3+4"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -86,11 +134,104 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "4.0.0"
+  flutter_plugin_android_lifecycle:
+    dependency: transitive
+    description:
+      name: flutter_plugin_android_lifecycle
+      sha256: "1c2b787f99bdca1f3718543f81d38aa1b124817dfeb9fb196201bea85b6134bf"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.26"
   flutter_test:
     dependency: "direct dev"
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_web_plugins:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.0"
+  http:
+    dependency: transitive
+    description:
+      name: http
+      sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.4.0"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.0.2"
+  image_picker:
+    dependency: "direct main"
+    description:
+      name: image_picker
+      sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.2"
+  image_picker_android:
+    dependency: transitive
+    description:
+      name: image_picker_android
+      sha256: "82652a75e3dd667a91187769a6a2cc81bd8c111bbead698d8e938d2b63e5e89a"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.8.12+21"
+  image_picker_for_web:
+    dependency: transitive
+    description:
+      name: image_picker_for_web
+      sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.6"
+  image_picker_ios:
+    dependency: transitive
+    description:
+      name: image_picker_ios
+      sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.8.12+2"
+  image_picker_linux:
+    dependency: transitive
+    description:
+      name: image_picker_linux
+      sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.1+2"
+  image_picker_macos:
+    dependency: transitive
+    description:
+      name: image_picker_macos
+      sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.1+2"
+  image_picker_platform_interface:
+    dependency: transitive
+    description:
+      name: image_picker_platform_interface
+      sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.10.1"
+  image_picker_windows:
+    dependency: transitive
+    description:
+      name: image_picker_windows
+      sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.2.1+1"
   leak_tracker:
     dependency: transitive
     description:
@@ -147,6 +288,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.15.0"
+  mime:
+    dependency: transitive
+    description:
+      name: mime
+      sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.0"
   nested:
     dependency: transitive
     description:
@@ -163,6 +312,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.9.0"
+  plugin_platform_interface:
+    dependency: transitive
+    description:
+      name: plugin_platform_interface
+      sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.8"
   provider:
     dependency: transitive
     description:
@@ -224,6 +381,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.7.2"
+  typed_data:
+    dependency: transitive
+    description:
+      name: typed_data
+      sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.4.0"
   vector_math:
     dependency: transitive
     description:
@@ -240,6 +405,30 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "14.2.5"
+  web:
+    dependency: transitive
+    description:
+      name: web
+      sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.1"
+  web_socket:
+    dependency: transitive
+    description:
+      name: web_socket
+      sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.1"
+  web_socket_channel:
+    dependency: "direct main"
+    description:
+      name: web_socket_channel
+      sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.3"
 sdks:
   dart: ">=3.5.4 <4.0.0"
-  flutter: ">=3.18.0-18.0.pre.54"
+  flutter: ">=3.24.0"

+ 2 - 0
bloc_example/pubspec.yaml

@@ -36,6 +36,8 @@ dependencies:
   cupertino_icons: ^1.0.8
   flutter_bloc: ^8.0.0
   bloc: ^8.0.0
+  web_socket_channel: ^3.0.3
+  image_picker: ^1.1.2
 
 dev_dependencies:
   flutter_test:

+ 3 - 0
bloc_example/windows/flutter/generated_plugin_registrant.cc

@@ -6,6 +6,9 @@
 
 #include "generated_plugin_registrant.h"
 
+#include <file_selector_windows/file_selector_windows.h>
 
 void RegisterPlugins(flutter::PluginRegistry* registry) {
+  FileSelectorWindowsRegisterWithRegistrar(
+      registry->GetRegistrarForPlugin("FileSelectorWindows"));
 }

+ 1 - 0
bloc_example/windows/flutter/generated_plugins.cmake

@@ -3,6 +3,7 @@
 #
 
 list(APPEND FLUTTER_PLUGIN_LIST
+  file_selector_windows
 )
 
 list(APPEND FLUTTER_FFI_PLUGIN_LIST