flutter - 在 _WidgetsAppState 中找不到路由 RouteSettings("todoscreen", Instance of 'ScreenArguments') 的生成器。扑
问题描述
好的,所以这个错误很奇怪的原因是,直到我在我的项目中添加了蓝牙功能,它才出现。如果我不能解决这个问题,我可能不得不从头开始制作我的应用程序作为一个颤振的初学者,我花了几个星期才走到这一步。让我简单描述一下我的应用:它是一款搭配可穿戴手环的健康应用。登录后,提示用户连接设备,一旦连接,返回 HomeScreen(),显示 arduino 通过蓝牙发送的温度和脉冲数据。目前,pulse 是硬编码的,但 temp 从传感器发送到应用程序,并更新到 firestore,然后检索和显示。因此,该应用程序既有用户界面,也有管理员界面。
这是 main.dart
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:vitality/screens/btInitialize.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:vitality/screens/welcome.dart';
import 'package:vitality/components/route.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Color(0xff222831),
accentColor: Color(0xff00adb5),
textTheme: TextTheme(
headline1: TextStyle(
fontSize: 25.0, fontFamily: 'Montserrat', color: Colors.black),
headline2: TextStyle(
fontSize: 60.0, fontFamily: 'CrimsonText', color: Colors.black),
headline3: TextStyle(
fontSize: 30.0, fontFamily: 'Lora', color: Colors.white),
headline4: TextStyle(
fontSize: 30.0, fontFamily: 'Montserrat', color: Colors.white),
headline5: TextStyle(
fontSize: 85.0, fontFamily: 'Montserrat', color: Colors.black),
headline6: TextStyle(
fontSize: 20.0, fontFamily: 'Montserrat', color: Colors.white),
),
),
initialRoute: Welcome.id,
onGenerateRoute: RouteGen.generateRoute,
);
}
}
这是 route.dart
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:vitality/screens/login.dart';
import 'package:vitality/screens/homescreen.dart';
import 'package:vitality/screens/chatbot.dart';
import 'package:vitality/screens/todo.dart';
import 'package:vitality/screens/welcome.dart';
import 'package:vitality/screens/register.dart';
import 'package:vitality/components/ScreenArguments.dart';
import 'package:vitality/screens/btInitialize.dart';
import 'package:vitality/components/HomeArguments.dart';
class RouteGen {
static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments;
switch (settings.name) {
case btInit.id:
return MaterialPageRoute(builder: (BuildContext context) {
final argument = args as ScreenArguments;
return btInit(
docid: argument.docid,
isCaretaker: argument.isCaretaker,
);
});
case Welcome.id:
return MaterialPageRoute(builder: (_) => Welcome());
case HomeScreen.id:
return MaterialPageRoute(builder: (BuildContext context) {
final argument = args as HomeArgs;
return HomeScreen(
docid: argument.docid,
isCaretaker: argument.isCaretaker,
currentDevice: argument.currentDevice);
});
case LoginScreen.id:
return MaterialPageRoute(builder: (_) => LoginScreen());
case Register.id:
return MaterialPageRoute(builder: (_) => Register());
case ChatBot.id:
return MaterialPageRoute(builder: (BuildContext context) {
final argument = args as ScreenArguments;
return ChatBot(
docid: argument.docid,
isCaretaker: argument.isCaretaker,
);
});
case Todo.id:
return MaterialPageRoute(builder: (BuildContext context) {
final argument = args as ScreenArguments;
return Todo(
docid: argument.docid,
isCaretaker: argument.isCaretaker,
);
});
}
}
}
因为我必须传递多个参数,所以我有一个 screenarguments 类来传递 docid,并且在屏幕之间传递 isCaregiver (bool)。并且第一个主屏幕需要传递一个设备参数,所以我只为家里做了一个不同的类。他们在这里
class ScreenArguments {
final String docid;
final bool isCaretaker;
ScreenArguments({this.docid, this.isCaretaker});
}
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
class HomeArgs {
final String docid;
final bool isCaretaker;
final BluetoothDevice currentDevice;
HomeArgs({this.docid, this.isCaretaker, this.currentDevice});
}
当登录按钮被按下时,这是它进入 btinitialize.dart 的屏幕
import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:vitality/screens/connection.dart';
import 'package:vitality/screens/homescreen.dart';
class btInit extends StatelessWidget {
final String docid;
final bool isCaretaker;
static const String id = 'btinit';
btInit({@required this.docid, @required this.isCaretaker});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: FutureBuilder(
future: FlutterBluetoothSerial.instance.requestEnable(),
builder: (context, future) {
if (future.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Container(
height: double.infinity,
child: Center(
child: Icon(
Icons.bluetooth_disabled,
size: 200.0,
color: Colors.blue,
),
),
),
);
} else if (future.connectionState == ConnectionState.done) {
print('bluetooth turned on');
// return MyHomePage(title: 'Flutter Demo Home Page');
return Home(docid: docid, isCaretaker: isCaretaker);
} else {
return Home(docid: docid, isCaretaker: isCaretaker);
}
},
// child: MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class Home extends StatelessWidget {
final String docid;
final bool isCaretaker;
Home({this.docid, this.isCaretaker});
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
toolbarHeight: 50.0,
centerTitle: true,
title: Text(
'HEALTH TRACKER',
style: Theme.of(context).textTheme.headline4,
)),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://www.setaswall.com/wp-content/uploads/2017/06/Blur-Phone-Wallpaper-1080x2340-011-340x550.jpg'),
fit: BoxFit.cover,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(.7), BlendMode.dstATop),
)),
child: SelectBondedDevicePage(
Upload: (device1) {
BluetoothDevice device = device1;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return HomeScreen(
docid: docid,
isCaretaker: isCaretaker,
currentDevice: device,
);
},
),
);
},
),
),
));
}
}
首先它要求用户打开蓝牙。然后它返回类 SELECTBONDEDDEVICEPAGE 这是一个设备和一个按钮,当按下时,带你到主屏幕。 connection.dart 这个页面是发现设备的代码。这里没有太多 UI 的东西。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
enum _DeviceAvailability {
maybe,
yes,
}
class _DeviceWithAvailability extends BluetoothDevice {
BluetoothDevice device;
_DeviceAvailability availability;
int rssi;
_DeviceWithAvailability(this.device, this.availability, [this.rssi]);
}
class SelectBondedDevicePage extends StatefulWidget {
final bool checkAvailability;
final Function Upload;
final String docid;
final bool isCaretaker;
static const String id = 'connect';
const SelectBondedDevicePage(
{this.checkAvailability = true,
this.Upload,
this.docid,
this.isCaretaker});
@override
_SelectBondedDevicePage createState() => new _SelectBondedDevicePage();
}
class _SelectBondedDevicePage extends State<SelectBondedDevicePage> {
List<_DeviceWithAvailability> devices = List<_DeviceWithAvailability>();
// Availability
StreamSubscription<BluetoothDiscoveryResult> _discoveryStreamSubscription;
bool _isDiscovering;
_SelectBondedDevicePage();
@override
void initState() {
super.initState();
_isDiscovering = widget.checkAvailability;
if (_isDiscovering) {
_startDiscovery();
}
// Setup a list of the bonded devices
FlutterBluetoothSerial.instance
.getBondedDevices()
.then((List<BluetoothDevice> bondedDevices) {
setState(() {
devices = bondedDevices
.map(
(device) => _DeviceWithAvailability(
device,
widget.checkAvailability
? _DeviceAvailability.maybe
: _DeviceAvailability.yes,
),
)
.toList();
});
});
}
void _startDiscovery() {
_discoveryStreamSubscription =
FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
setState(() {
Iterator i = devices.iterator;
while (i.moveNext()) {
var _device = i.current;
if (_device.device == r.device) {
_device.availability = _DeviceAvailability.yes;
_device.rssi = r.rssi;
}
}
});
});
_discoveryStreamSubscription.onDone(() {
setState(() {
_isDiscovering = false;
});
});
}
@override
void dispose() {
// Avoid memory leak (`setState` after dispose) and cancel discovery
_discoveryStreamSubscription?.cancel();
super.dispose();
}
//build returns list of bletooth devices entries (name, and connect button) with an on tap method Upload()
@override
Widget build(BuildContext context) {
List<BluetoothDeviceListEntry> list = devices
.map(
(_device) => BluetoothDeviceListEntry(
device: _device.device,
onTap: () {
widget.Upload(_device.device);
},
),
)
.toList();
return ListView(
children: list,
);
}
}
class BluetoothDeviceListEntry extends StatelessWidget {
final Function onTap;
final BluetoothDevice device;
BluetoothDeviceListEntry({this.onTap, @required this.device});
@override
Widget build(BuildContext context) {
return ListTile(
onTap: onTap,
leading: Icon(Icons.devices),
title: Text(device.name ?? "Unknown device"),
subtitle: Text(device.address.toString()),
trailing: FlatButton(
child: Text('Connect'),
onPressed: onTap,
color: Colors.transparent,
),
);
}
}
最后,当按下按钮时,它会进入主屏幕,参数为 docid、isCaretaker 和 hte device。 homescreen.dart
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:typed_data';
import 'package:vitality/components/bottomAppBar.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:vitality/components/biom.dart';
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
class HomeScreen extends StatefulWidget {
static const String id = 'home_screen';
final String docid;
final bool isCaretaker;
final BluetoothDevice currentDevice;
HomeScreen(
{@required this.docid,
@required this.isCaretaker,
@required this.currentDevice});
@override
_HomeScreenState createState() => _HomeScreenState();
}
_callNumber() async {
const number = '8606535166'; //set the number here
bool res = await FlutterPhoneDirectCaller.callNumber(number);
}
class _HomeScreenState extends State<HomeScreen> {
final auth = FirebaseAuth.instance;
var pulse;
var temp;
static final clientID = 0;
BluetoothConnection connection;
String _messageBuffer = '';
bool isConnecting = true;
bool get isConnected => connection != null && connection.isConnected;
bool isDisconnecting = false;
@override
void initState() {
super.initState();
BluetoothConnection.toAddress(widget.currentDevice.address)
.then((_connection) {
print('Connected to the device');
print('device is ${widget.currentDevice}');
connection = _connection;
setState(() {
isConnecting = false;
isDisconnecting = false;
});
connection.input.listen(_onDataReceived).onDone(() {
if (isDisconnecting) {
print('Disconnecting locally!');
} else {
print('Disconnected remotely!');
}
if (this.mounted) {
setState(() {});
}
});
}).catchError((error) {
print('Cannot connect, exception occurred');
print(error);
});
}
@override
void dispose() {
// Avoid memory leak (`setState` after dispose) and disconnect
if (isConnected) {
isDisconnecting = true;
connection.dispose();
connection = null;
}
super.dispose();
}
void _onDataReceived(Uint8List data) {
// Allocate buffer for parsed data
int backspacesCounter = 0;
data.forEach((byte) {
if (byte == 8 || byte == 127) {
backspacesCounter++;
}
});
Uint8List buffer = Uint8List(data.length - backspacesCounter);
int bufferIndex = buffer.length;
// Apply backspace control character
backspacesCounter = 0;
for (int i = data.length - 1; i >= 0; i--) {
if (data[i] == 8 || data[i] == 127) {
backspacesCounter++;
} else {
if (backspacesCounter > 0) {
backspacesCounter--;
} else {
buffer[--bufferIndex] = data[i];
}
}
}
// Create message if there is new line character
String dataString = String.fromCharCodes(buffer);
print('$dataString');
changePulse(int.parse(dataString));
int index = buffer.indexOf(13);
if (~index != 0) {
setState(() {
_messageBuffer = dataString.substring(index);
//print('In message buffer is $_messageBuffer');
});
} else {
_messageBuffer = (backspacesCounter > 0
? _messageBuffer.substring(
0, _messageBuffer.length - backspacesCounter)
: _messageBuffer + dataString);
}
}
void _sendMessage(String text) async {
text = text.trim();
if (text.length > 0) {
try {
connection.output.add(utf8.encode(text + "\r\n"));
await connection.output.allSent;
} catch (e) {
// Ignore error, but notify state
setState(() {});
}
}
}
changePulse(int dataString) {
main
.doc(widget.docid)
.update({'pulse': dataString})
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}
@override
Widget build(BuildContext context) {
print(
'in homescreen each iis ${widget.docid}, ${widget.isCaretaker},${widget.currentDevice}');
_sendMessage('1');
print('got here');
CollectionReference main = FirebaseFirestore.instance.collection('maindb');
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.transparent,
toolbarHeight: 50.0,
centerTitle: true,
title: Text(
'HEALTH TRACKER',
style: Theme.of(context).textTheme.headline4,
)),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://www.setaswall.com/wp-content/uploads/2017/06/Blur-Phone-Wallpaper-1080x2340-011-340x550.jpg'),
fit: BoxFit.cover,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(.7), BlendMode.dstATop),
)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(height: 100.0),
Text(widget.docid),
Text({widget.isCaretaker}.toString()),
biom(which: 'pulse', image: 'pulse', docid: widget.docid),
RoundBorderText(text: 'PULSE'),
biom(which: 'temperature', image: 'temper', docid: widget.docid),
RoundBorderText(text: 'TEMPERATURE'),
SizedBox(height: 30.0),
FlatButton(
child: Text('test call'),
onPressed: () async {
_callNumber();
})
]),
),
**bottomNavigationBar**: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://www.setaswall.com/wp-content/uploads/2017/06/Blur-Phone-Wallpaper-1080x2340-011-340x550.jpg'),
fit: BoxFit.cover,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(1), BlendMode.dstATop),
)),
child: bottomAppBar()),
);
}
}
class RoundBorderText extends StatelessWidget {
final String text;
RoundBorderText({this.text});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(
left: 40.0, right: 40.0, top: 8.0, bottom: 8.0),
decoration: BoxDecoration(
// ),
borderRadius: BorderRadius.all(Radius.circular(20))),
child: Text(text, style: Theme.of(context).textTheme.headline1));
}
}
基本上,当它从 arduino 接收数据时,它会将其添加到 firestore 并在屏幕上显示。底部是一个bottomappbar,我将其提取到一个类中。
问题就在这里。
import 'package:flutter/material.dart';
import 'package:vitality/screens/login.dart';
import 'package:vitality/components/ScreenArguments.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
class bottomAppBar extends StatefulWidget {
final String id;
bottomAppBar({this.id});
@override
_bottomAppBarState createState() => _bottomAppBarState();
}
class _bottomAppBarState extends State<bottomAppBar> {
@override
Widget build(BuildContext context) {
print('id in bottom is ${widget.id}');
return BottomAppBar(
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(Icons.list),
color: Colors.white,
onPressed: () {
print('to do pressed');
print(docid);
print(isCaretaker.toString());
Navigator.of(context).pushNamed('todoscreen',
arguments: ScreenArguments(
docid: docid, isCaretaker: isCaretaker));
}),
IconButton(
icon: Icon(Icons.data_usage),
color: Colors.white,
onPressed: () {
Navigator.of(context).pushNamed('home_screen',
arguments: ScreenArguments(
docid: widget.id, isCaretaker: isCaretaker));
}),
IconButton(
icon: Icon(Icons.chat),
color: Colors.white,
onPressed: () {
Navigator.of(context).pushNamed('chat_screen',
arguments: ScreenArguments(
docid: widget.id, isCaretaker: isCaretaker));
}),
],
),
);
}
}
底部栏由 3 个图标组成,一个提醒图标,一个主屏幕图标和一个聊天机器人。我们目前在主屏幕中,但是当按下 todo 图标时,它应该会进入屏幕。这在我将其放入蓝牙代码之前有效,但现在我收到此错误:
在 _WidgetsAppState 中找不到路由 RouteSettings("chat_screen", Instance of 'ScreenArguments') 的生成器。
待办事项屏幕和聊天屏幕。
这里的 todo.dart 代码只是这样你就可以看到它已经用必要的参数初始化了
bool temp;
var todoid;
int number = 0;
final auth = FirebaseAuth.instance;
Stream collectionStream =
FirebaseFirestore.instance.collection('todo').snapshots();
CollectionReference main = FirebaseFirestore.instance.collection('maindb');
CollectionReference todo = FirebaseFirestore.instance.collection('todo');
final myController = TextEditingController();
class Todo extends StatefulWidget {
final String docid;
final bool isCaretaker;
Todo({this.docid, this.isCaretaker});
@override
_TodoState createState() => _TodoState();
static const String id = 'todoscreen';
}
任何帮助将不胜感激,因为我完全不知道出了什么问题。(蓝牙代码正在工作)
解决方案
在使用Navigator.of(context)
时,Flutter 会遍历 widget 树中的祖先以找到最近的Navigator
.
现在,您实际上并没有Navigator
在树中提供任何小部件,那么您Navigator
来自哪里?
就是这样MaterialApp
。
现在,您的主MaterialApp
目录位于根目录。
但是如果你检查你的btInit
小部件,你已经在其中声明了另一个MaterialApp
。因此,当您调用时pushNamed('todoscreen')
,它实际上是Navigator
从MaterialApp
您的btInit
小部件而不是主小部件中获取的。
由于您仅onGenerateRoute
在 main 上定义MaterialApp
,因此无法解析对todoscreen
路由名称的请求。
删除MaterialApp
您的btInit
小部件内部,这应该得到解决。
推荐阅读
- json - 如何获取特定的 JSON 项目?
- html - 水平滚动书签
- javascript - 无法使用 aws-sdk 获取 AWS 机密 - 我要么丢失凭证错误和一个对象
- python - ValueError:未知的池类型
- r - 如何在众多 CSV 中测试某些列名的存在
- javascript - Accessing any element in a useRef is throwing 'undefined'
- c# - 在 CustomEntry 中将视觉对象设置为材质
- oracle - 如何连接到 PDB 并在 oracle 中创建表空间用户备份?
- express - 我无法在 ejs 中显示数据库的输出值?
- c# - 无法使用 EnableRaisingEvents 打开图片