更换Dialogflow凭证后出现LateInitializationError问题求助
问题:替换Dialogflow凭证后出现LateInitializationError错误
上周代码运行正常,替换过期的新凭证后,代码抛出以下错误:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: LateInitializationError: Field 'dialogflow' has not been initialized
相关代码如下:
import 'package:dialogflow_grpc/dialogflow_grpc.dart'; import 'package:dialogflow_grpc/generated/google/cloud/dialogflow/v2beta1/session.pb.dart'; import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class Chat extends StatefulWidget { Chat({Key? key}) : super(key: key); @override _ChatState createState() => _ChatState(); } class _ChatState extends State<Chat> { final List<ChatMessage> _messages = <ChatMessage>[]; final TextEditingController _textController = TextEditingController(); late DialogflowGrpcV2Beta1 dialogflow; @override void initState() { super.initState(); initPlugin(); } Future<void> initPlugin()async{ final serviceAccount = ServiceAccount.fromString( '${(await rootBundle.loadString('assets/credentials2.json'))}'); // Create a DialogflowGrpc Instance dialogflow = DialogflowGrpcV2Beta1.viaServiceAccount(serviceAccount); } void handleSubmitted(text) async { print(text); _textController.clear(); ChatMessage message = ChatMessage( text: text, name: "You", type: true, ); setState(() { _messages.insert(0, message); }); DetectIntentResponse data = await dialogflow.detectIntent(text, 'en-US'); String fulfillmentText = data.queryResult.fulfillmentText; if(fulfillmentText.isNotEmpty) { ChatMessage botMessage = ChatMessage( text: fulfillmentText, name: "Bot", type: false, ); setState(() { _messages.insert(0, botMessage); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Chatbot Page'), ), body: Column(children: <Widget>[ Flexible( child: ListView.builder( padding: EdgeInsets.all(8.0), reverse: true, itemBuilder: (_, int index) => _messages[index], itemCount: _messages.length, )), Divider(height: 1.0), Container( decoration: BoxDecoration(color: Theme.of(context).cardColor), child: IconTheme( data: IconThemeData(color: Theme.of(context).accentColor), child: Container( margin: const EdgeInsets.symmetric(horizontal: 8.0), child: Row( children: <Widget>[ Flexible( child: TextField( controller: _textController, onSubmitted: handleSubmitted, decoration: InputDecoration.collapsed(hintText: "Send a message to begin"), ), ), Container( margin: EdgeInsets.symmetric(horizontal: 4.0), child: IconButton( icon: Icon(Icons.send), onPressed: () => handleSubmitted(_textController.text), ), ), ], ), ), ) ), ]), ); } } class ChatMessage extends StatelessWidget { ChatMessage({required this.text, required this.name, required this.type}); final String text; final String name; final bool type; List<Widget> otherMessage(context) { return <Widget>[ new Container( margin: const EdgeInsets.only(right: 16.0), child: CircleAvatar(child: new Text('B')), ), new Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text(this.name, style: TextStyle(fontWeight: FontWeight.bold)), Container( margin: const EdgeInsets.only(top: 5.0), child: Text(text), ), ], ), ), ]; } List<Widget> myMessage(context) { return <Widget>[ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ Text(this.name, style: Theme.of(context).textTheme.subtitle1), Container( margin: const EdgeInsets.only(top: 5.0), child: Text(text), ), ], ), ), Container( margin: const EdgeInsets.only(left: 16.0), child: CircleAvatar( child: Text( this.name[0], style: TextStyle(fontWeight: FontWeight.bold), )), ), ]; } @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(vertical: 10.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: this.type ? myMessage(context) : otherMessage(context), ), ); } }
原因分析与解决办法
核心原因
initPlugin()是异步方法,initState中调用时不会等待它执行完毕。替换新凭证后,凭证加载、Dialogflow实例初始化的耗时可能变长,若用户在初始化完成前发送消息,就会触发LateInitializationError。
解决方法1:添加加载状态,禁用输入直到初始化完成
在状态类中新增加载状态变量,初始化完成前禁止用户交互:
class _ChatState extends State<Chat> { final List<ChatMessage> _messages = <ChatMessage>[]; final TextEditingController _textController = TextEditingController(); late DialogflowGrpcV2Beta1 dialogflow; bool _isInitializing = true; // 新增加载状态 @override void initState() { super.initState(); _initPlugin(); } Future<void> _initPlugin() async { try { final serviceAccount = ServiceAccount.fromString( '${(await rootBundle.loadString('assets/credentials2.json'))}'); dialogflow = DialogflowGrpcV2Beta1.viaServiceAccount(serviceAccount); } catch (e) { // 处理凭证加载失败的提示 setState(() { _messages.insert(0, ChatMessage(text: "初始化失败:$e", name: "系统", type: false)); }); } finally { setState(() { _isInitializing = false; // 初始化完成,更新状态 }); } } // ... 其他代码不变 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Chatbot Page')), body: Column(children: <Widget>[ Flexible(child: ListView.builder( padding: EdgeInsets.all(8.0), reverse: true, itemBuilder: (_, int index) => _messages[index], itemCount: _messages.length, )), Divider(height: 1.0), Container( decoration: BoxDecoration(color: Theme.of(context).cardColor), child: IconTheme( data: IconThemeData(color: Theme.of(context).accentColor), child: Container( margin: const EdgeInsets.symmetric(horizontal: 8.0), child: Row( children: <Widget>[ Flexible( child: TextField( controller: _textController, onSubmitted: _isInitializing ? null : handleSubmitted, decoration: InputDecoration.collapsed( hintText: _isInitializing ? "正在初始化..." : "Send a message to begin" ), enabled: !_isInitializing, // 禁用未初始化状态的输入框 ), ), Container( margin: EdgeInsets.symmetric(horizontal: 4.0), child: IconButton( icon: Icon(Icons.send), onPressed: _isInitializing ? null : () => handleSubmitted(_textController.text), disabledColor: Colors.grey, ), ), ], ), ), ) ), ]), ); } }
解决方法2:将dialogflow改为可空类型,调用前检查
把late声明改为可空类型,在发送消息前判断是否初始化完成:
class _ChatState extends State<Chat> { final List<ChatMessage> _messages = <ChatMessage>[]; final TextEditingController _textController = TextEditingController(); DialogflowGrpcV2Beta1? dialogflow; // 改为可空类型 @override void initState() { super.initState(); _initPlugin(); } Future<void> _initPlugin() async { final serviceAccount = ServiceAccount.fromString( '${(await rootBundle.loadString('assets/credentials2.json'))}'); dialogflow = DialogflowGrpcV2Beta1.viaServiceAccount(serviceAccount); } void handleSubmitted(text) async { if (dialogflow == null) { setState(() { _messages.insert(0, ChatMessage(text: "正在初始化,请稍后再试", name: "系统", type: false)); }); _textController.clear(); return; } // 原有业务逻辑 print(text); _textController.clear(); ChatMessage message = ChatMessage( text: text, name: "You", type: true, ); setState(() { _messages.insert(0, message); }); DetectIntentResponse data = await dialogflow!.detectIntent(text, 'en-US'); String fulfillmentText = data.queryResult.fulfillmentText; if(fulfillmentText.isNotEmpty) { ChatMessage botMessage = ChatMessage( text: fulfillmentText, name: "Bot", type: false, ); setState(() { _messages.insert(0, botMessage); }); } } // ... 其他代码不变 }
额外建议
- 确认新凭证文件
credentials2.json的路径是否正确,保证rootBundle.loadString能读取到文件 - 在
_initPlugin中添加日志打印,排查初始化过程中的问题:Future<void> _initPlugin() async { try { final creds = await rootBundle.loadString('assets/credentials2.json'); print("凭证加载成功:${creds.length}字符"); final serviceAccount = ServiceAccount.fromString(creds); dialogflow = DialogflowGrpcV2Beta1.viaServiceAccount(serviceAccount); print("Dialogflow实例初始化完成"); } catch (e) { print("初始化失败:$e"); } }
内容的提问来源于stack exchange,提问作者Lim
相关产品推荐
相关产品推荐

