首页 > 解决方案 > 在 _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 的东西。1 2

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。 3 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';
}

任何帮助将不胜感激,因为我完全不知道出了什么问题。(蓝牙代码正在工作)

标签: flutterdartbluetoothnavigation

解决方案


在使用Navigator.of(context)时,Flutter 会遍历 widget 树中的祖先以找到最近的Navigator.

现在,您实际上并没有Navigator在树中提供任何小部件,那么您Navigator来自哪里?

就是这样MaterialApp

现在,您的主MaterialApp目录位于根目录。

但是如果你检查你的btInit小部件,你已经在其中声明了另一个MaterialApp。因此,当您调用时pushNamed('todoscreen'),它实际上是NavigatorMaterialApp您的btInit小部件而不是主小部件中获取的。

由于您仅onGenerateRoute在 main 上定义MaterialApp,因此无法解析对todoscreen路由名称的请求。

删除MaterialApp您的btInit小部件内部,这应该得到解决。


推荐阅读