This Flutter code snippet demonstrates a simple yet effective integration of GPT-powered chat functionality into your Flutter app. Learn how to implement a conversational UI, send and receive messages, and customize the user experience.

class chat_gpt extends StatefulWidget {
  @override
  _chat_gptState createState() => _chat_gptState();
}

class _chat_gptState extends State<chat_gpt> {
  List<types.Message> _messages = [];
  final _user = const types.User(
    id: '82091008-a484-4a89-ae75-a22bf8d6f3ac',
  );
  var _filteredStringList = [];
  var _stringList = [];
  //从assets/prompts.json中读取数据,并转换为List<Map<String, String>>
  void loadprompts() async {
    String prompt = await rootBundle.loadString('assets/prompts.json');
    _stringList = json.decode(prompt);
    _filteredStringList = List.from(_stringList);
  }

  int _selectedIndex = -1;
  @override
  void initState() {
    super.initState();
    _loadMessages();
  }

  void _onSearch(String keyword) {
    SchedulerBinding.instance!.addPostFrameCallback((_) {
      setState(() {
        _filteredStringList = _stringList.where((item) {
          return item['act']!.contains(keyword) ||
              item['prompt']!.contains(keyword);
        }).toList();
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Chat with GPT'),
        //加入返回按鈕
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          onPressed: () {
            //跳转到成绩页面
            Navigator.push(context, MaterialPageRoute(builder: (context) {
              return HomePage();
            }));
          },
        ),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.settings),
            onPressed: () {
              showDialog(
                  context: context,
                  builder: (context) {
                    return Scaffold(
                        appBar: AppBar(
                          title: Text('自动注入'),
                          actions: [
                            IconButton(
                              icon: const Icon(Icons.search),
                              onPressed: () {
                                showSearch(
                                    context: context,
                                    delegate: _SearchBarDelegate(
                                        onSearch: _onSearch));
                              },
                            ),
                          ],
                        ),
                        body: ListView.builder(
                          itemCount: _filteredStringList.length,
                          itemBuilder: (BuildContext context, int index) {
                            String act = _filteredStringList[index]['act']!;
                            String prompt =
                                _filteredStringList[index]['prompt']!;
                            return RadioListTile(
                              title: Text('行为: $act'),
                              subtitle: Text('语句: $prompt'),
                              value: index,
                              groupValue: _selectedIndex,
                              onChanged: (int? value) {
                                setState(() {
                                  _selectedIndex = value!;                                  
                                });
                              },
                            );
                          },
                        ));
                  });
            },
          ),
        ],
      ),
      body: Chat(
        messages: _messages,
        onAttachmentPressed: _handleAttachmentPressed,
        onMessageTap: _handleMessageTap,
        onPreviewDataFetched: _handlePreviewDataFetched,
        onSendPressed: _handleSendPressed,
        showUserAvatars: true,
        showUserNames: true,
        user: _user,
      ),
    );
  }

  void _addMessage(types.Message message) {
    setState(() {
      _messages.insert(0, message);
    });
  }

  void _handleAttachmentPressed() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) => SafeArea(
        child: SizedBox(
          height: 144,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                  _handleImageSelection();
                },
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('Photo'),
                ),
              ),
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                  _handleFileSelection();
                },
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('File'),
                ),
              ),
              TextButton(
                onPressed: () => Navigator.pop(context),
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('Cancel'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _handleFileSelection() async {
    final result = await FilePicker.platform.pickFiles(
      type: FileType.any,
    );

    if (result != null && result.files.single.path != null) {
      final message = types.FileMessage(
        author: _user,
        createdAt: DateTime.now().millisecondsSinceEpoch,
        id: const Uuid().v4(),
        mimeType: lookupMimeType(result.files.single.path!),
        name: result.files.single.name,
        size: result.files.single.size,
        uri: result.files.single.path!,
      );

      _addMessage(message);
    }
  }

  void _handleImageSelection() async {
    final result = await ImagePicker().pickImage(
      imageQuality: 70,
      maxWidth: 1440,
      source: ImageSource.gallery,
    );

    if (result != null) {
      final bytes = await result.readAsBytes();
      final image = await decodeImageFromList(bytes);

      final message = types.ImageMessage(
        author: _user,
        createdAt: DateTime.now().millisecondsSinceEpoch,
        height: image.height.toDouble(),
        id: const Uuid().v4(),
        name: result.name,
        size: bytes.length,
        uri: result.path,
        width: image.width.toDouble(),
      );

      _addMessage(message);
    }
  }

  void _handleMessageTap(BuildContext _, types.Message message) async {
    if (message is types.FileMessage) {
      var localPath = message.uri;

      if (message.uri.startsWith('http')) {
        try {
          final index =
              _messages.indexWhere((element) => element.id == message.id);
          final updatedMessage =
              (_messages[index] as types.FileMessage).copyWith(
            isLoading: true,
          );

          setState(() {
            _messages[index] = updatedMessage;
          });

          final client = http.Client();
          final request = await client.get(Uri.parse(message.uri));
          final bytes = request.bodyBytes;
          final documentsDir = (await getApplicationDocumentsDirectory()).path;
          localPath = '$documentsDir/${message.name}';

          if (!File(localPath).existsSync()) {
            final file = File(localPath);
            await file.writeAsBytes(bytes);
          }
        } finally {
          final index =
              _messages.indexWhere((element) => element.id == message.id);
          final updatedMessage =
              (_messages[index] as types.FileMessage).copyWith(
            isLoading: null,
          );

          setState(() {
            _messages[index] = updatedMessage;
          });
        }
      }
    }
  }

  void _handlePreviewDataFetched(
    types.TextMessage message,
    types.PreviewData previewData,
  ) {
    final index = _messages.indexWhere((element) => element.id == message.id);
    final updatedMessage = (_messages[index] as types.TextMessage).copyWith(
      previewData: previewData,
    );

    setState(() {
      _messages[index] = updatedMessage;
    });
  }

  void _handleSendPressed(types.PartialText message) {
    final textMessage = types.TextMessage(
      author: _user,
      createdAt: DateTime.now().millisecondsSinceEpoch,
      id: const Uuid().v4(),
      text: message.text,
    );

    _addMessage(textMessage);
    print(message.text);
    //打印最近20条消息
    print(_messages.sublist(0, 20));
  }

  void _loadMessages() async {
    final response = await rootBundle.loadString('assets/messages.json');
    final messages = (jsonDecode(response) as List)
        .map((e) => types.Message.fromJson(e as Map<String, dynamic>))
        .toList();

    setState(() {
      _messages = messages;
    });
  }
}

class _SearchBarDelegate extends SearchDelegate<String> {
  final Function(String) onSearch;

  _SearchBarDelegate({required this.onSearch});

  @override
  List<Widget> buildActions(BuildContext context) {
    return [
      IconButton(
        icon: const Icon(Icons.clear),
        onPressed: () {
          query = '';
        },
      ),
    ];
  }

  @override
  Widget buildLeading(BuildContext context) {
    return IconButton(
      icon: const Icon(Icons.arrow_back),
      onPressed: () {
        close(context, '');
      },
    );
  }

  @override
  Widget buildResults(BuildContext context) {
    onSearch(query);
    return const SizedBox.shrink();
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    return const SizedBox.shrink();
  }
}

The error message indicates that there is an issue with calling setState() during the build phase of the widget tree. This can happen if you try to update the state of a widget while it is being constructed or rebuilt.

To fix this issue, you can try moving the setState() call to a later point in the lifecycle of the widget. For example, you can use the SchedulerBinding to schedule a callback to be executed after the current frame has finished building:

void _onSearch(String keyword) {
  SchedulerBinding.instance!.addPostFrameCallback((_) {
    setState(() {
      _filteredStringList = _stringList.where((item) {
        return item['act']!.contains(keyword) ||
            item['prompt']!.contains(keyword);
      }).toList();
    });
  });
}

This will ensure that the setState() call is executed after the widget has finished building, avoiding the error message.

Flutter Chat with GPT:  Integrate AI Conversations into Your App

原文地址: https://www.cveoy.top/t/topic/lQr7 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录