在 _WidgetsAppState 中找不到路由 RouteSettings("todoscreen",'ScreenArguments' 的实例)的生成器。扑

Could not find a generator for route RouteSettings("todoscreen", Instance of 'ScreenArguments') in the _WidgetsAppState. FLUTTER

好吧,这个错误很奇怪的原因是它在我向我的项目添加蓝牙功能之前不存在。如果我不能解决这个问题,作为一个 flutter 初学者,我可能不得不从头开始制作我的应用程序,我花了几周的时间才走到这一步。让我简单描述一下我的应用程序:它是一款配有可穿戴手环的健康应用程序。登录后,系统会提示用户连接到设备,一旦连接,就会返回 HomeScreen(),其中显示了通过蓝牙从 arduino 发送的温度和脉冲数据。目前,pulse 是硬编码的,但温度从传感器发送到应用程序,并更新到 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,
          );
        });
    }
  }
}

因为我必须传递多个参数,所以我有一个屏幕参数 class 在屏幕之间传递 docid 和 isCaregiver (bool)。第一个主屏幕需要传递一个设备参数,所以我只为家里做了一个不同的 class。 他们在这里

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,
                  );
                },
              ),
            );
          },
        ),
      ),
    ));
  }
}

首先它要求用户打开蓝牙。然后它 returns class 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 设备的主屏幕。 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,我将其提取到 class.

问题出在这里。

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 个图标组成,一个提醒图标,一个主屏幕和一个聊天机器人。我们目前在主屏幕上,但是当按下待办事项图标时,它应该会转到该屏幕。这在我将其放入蓝牙代码之前有效,但现在我收到此错误:

无法在 _WidgetsAppState 中找到路由 RouteSettings(“chat_screen”,'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') 时,它实际上是从 btInit 小部件的 MaterialApp 获取 Navigator 而不是主小部件。

因为您只在主 MaterialApp 上定义了 onGenerateRoute,它无法解析对 todoscreen 路由名称的请求。

删除 btInit 小部件中的 MaterialApp,这应该可以解决。