Flutter Chat GPT:使用 flutter_chat_ui 实现聊天机器人
import 'package:flutter/material.dart';
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:file_picker/file_picker.dart';
import 'package:http/http.dart' as http;
import 'package:muse_nepu_course/home.dart';
import 'package:uuid/uuid.dart';
import 'package:path_provider/path_provider.dart';
import 'package:mime/mime.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
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() {
String prompt = rootBundle.loadString('assets/prompts.json').toString();
_stringList = json.decode(prompt);
_filteredStringList = List.from(_stringList);
}
int _selectedIndex = -1;
@override
void initState() {
super.initState();
_loadMessages();
}
void _onSearch(String keyword) {
setState(() {
_filteredStringList = _stringList.where((item) {
return item['act']!.contains(keyword) ||
item['prompt']!.contains(keyword);
}).toList();
});
}
@override
Widget build(BuildContext context) {
loadprompts();
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();
}
}
这个错误是由于在 initState 中调用了 setState 导致的,尝试将 loadprompts 移到 build 中调用,或者将 loadprompts 改为同步调用即可解决此问题。例如:将 loadprompts 中的 await 去掉,改为直接读取即可。
原文地址: https://www.cveoy.top/t/topic/lQr3 著作权归作者所有。请勿转载和采集!