在没有默认构造函数的情况下初始化空安全变量?

Initialize a null safe variable without a default constructor?

我确定以前有人问过这个问题,但我什至不知道如何正确地表达它。

我正在尝试将蓝牙设备数据导入我的 flutter 应用程序。我发现的示例要么使用非 null 安全版本的 dart 代码,要么隐藏所有重要细节。

我正在尝试从头开始构建一个非常简单的原型,以便更好地掌握事物。

我想制作一个基于 notifyListeners() 进行更新的有状态小部件。我的想法是从“noName 蓝牙设备”开始,然后一旦连接了设备,我就可以更新对象并显示连接设备的名称。

我一直 运行 进入同样的障碍,我无法越过它。我想制作一个默认的蓝牙设备,但该设备没有默认构造函数。由于空安全性,默认设备不能为空。

谁能帮我解决这个问题。有些事情我知道我从根本上误解了,但我不知道从哪里开始。

下面的代码使 ChangeNotifierProvider 成为我的 BlueTesting 小部件的父级,它应该显示连接的蓝牙设备的详细信息(我还没有编写所有代码)。

BTDevices class 应该更新蓝牙设备对象,并通知应用程序将更新的数据从“默认空设备”显示到新连接的设备。

感谢您的帮助!

import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:provider/provider.dart';

void main() => runApp(
    ChangeNotifierProvider(create: (_) => BTDevices(), child: BlueTesting()));

class BTDevices extends ChangeNotifier {
  BluetoothDevice device = BluetoothDevice();
  void setDevice(BluetoothDevice newDevice) {
    device = newDevice;
    notifyListeners();
  }
}

更新:

我尝试更新上面的代码以设置:

BluetoothDevice device = null;

无法将 'Null' 类型的值分配给 'BluetoothDevice' 类型的变量。 尝试更改变量的类型,或将右侧类型转换为 'BluetoothDevice'.dartinvalid_assignment

这里是关于 BluetoothDevice 定义的信息:

part of flutter_blue;

class BluetoothDevice {
  final DeviceIdentifier id;
  final String name;
  final BluetoothDeviceType type;

  BluetoothDevice.fromProto(protos.BluetoothDevice p)
      : id = new DeviceIdentifier(p.remoteId),
        name = p.name,
        type = BluetoothDeviceType.values[p.type.value];

  BehaviorSubject<bool> _isDiscoveringServices = BehaviorSubject.seeded(false);
  Stream<bool> get isDiscoveringServices => _isDiscoveringServices.stream;

  /// Establishes a connection to the Bluetooth Device.
  Future<void> connect({
    Duration? timeout,
    bool autoConnect = true,
  }) async {
    var request = protos.ConnectRequest.create()
      ..remoteId = id.toString()
      ..androidAutoConnect = autoConnect;

    Timer? timer;
    if (timeout != null) {
      timer = Timer(timeout, () {
        disconnect();
        throw TimeoutException('Failed to connect in time.', timeout);
      });
    }

    await FlutterBlue.instance._channel
        .invokeMethod('connect', request.writeToBuffer());

    await state.firstWhere((s) => s == BluetoothDeviceState.connected);

    timer?.cancel();

    return;
  }

  /// Cancels connection to the Bluetooth Device
  Future disconnect() =>
      FlutterBlue.instance._channel.invokeMethod('disconnect', id.toString());

  BehaviorSubject<List<BluetoothService>> _services =
      BehaviorSubject.seeded([]);

  /// Discovers services offered by the remote device as well as their characteristics and descriptors
  Future<List<BluetoothService>> discoverServices() async {
    final s = await state.first;
    if (s != BluetoothDeviceState.connected) {
      return Future.error(new Exception(
          'Cannot discoverServices while device is not connected. State == $s'));
    }
    var response = FlutterBlue.instance._methodStream
        .where((m) => m.method == "DiscoverServicesResult")
        .map((m) => m.arguments)
        .map((buffer) => new protos.DiscoverServicesResult.fromBuffer(buffer))
        .where((p) => p.remoteId == id.toString())
        .map((p) => p.services)
        .map((s) => s.map((p) => new BluetoothService.fromProto(p)).toList())
        .first
        .then((list) {
      _services.add(list);
      _isDiscoveringServices.add(false);
      return list;
    });

    await FlutterBlue.instance._channel
        .invokeMethod('discoverServices', id.toString());

    _isDiscoveringServices.add(true);

    return response;
  }

  /// Returns a list of Bluetooth GATT services offered by the remote device
  /// This function requires that discoverServices has been completed for this device
  Stream<List<BluetoothService>> get services async* {
    yield await FlutterBlue.instance._channel
        .invokeMethod('services', id.toString())
        .then((buffer) =>
            new protos.DiscoverServicesResult.fromBuffer(buffer).services)
        .then((i) => i.map((s) => new BluetoothService.fromProto(s)).toList());
    yield* _services.stream;
  }

  /// The current connection state of the device
  Stream<BluetoothDeviceState> get state async* {
    yield await FlutterBlue.instance._channel
        .invokeMethod('deviceState', id.toString())
        .then((buffer) => new protos.DeviceStateResponse.fromBuffer(buffer))
        .then((p) => BluetoothDeviceState.values[p.state.value]);

    yield* FlutterBlue.instance._methodStream
        .where((m) => m.method == "DeviceState")
        .map((m) => m.arguments)
        .map((buffer) => new protos.DeviceStateResponse.fromBuffer(buffer))
        .where((p) => p.remoteId == id.toString())
        .map((p) => BluetoothDeviceState.values[p.state.value]);
  }

  /// The MTU size in bytes
  Stream<int> get mtu async* {
    yield await FlutterBlue.instance._channel
        .invokeMethod('mtu', id.toString())
        .then((buffer) => new protos.MtuSizeResponse.fromBuffer(buffer))
        .then((p) => p.mtu);

    yield* FlutterBlue.instance._methodStream
        .where((m) => m.method == "MtuSize")
        .map((m) => m.arguments)
        .map((buffer) => new protos.MtuSizeResponse.fromBuffer(buffer))
        .where((p) => p.remoteId == id.toString())
        .map((p) => p.mtu);
  }

  /// Request to change the MTU Size
  /// Throws error if request did not complete successfully
  Future<void> requestMtu(int desiredMtu) async {
    var request = protos.MtuSizeRequest.create()
      ..remoteId = id.toString()
      ..mtu = desiredMtu;

    return FlutterBlue.instance._channel
        .invokeMethod('requestMtu', request.writeToBuffer());
  }

  /// Indicates whether the Bluetooth Device can send a write without response
  Future<bool> get canSendWriteWithoutResponse =>
      new Future.error(new UnimplementedError());

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is BluetoothDevice &&
          runtimeType == other.runtimeType &&
          id == other.id;

  @override
  int get hashCode => id.hashCode;

  @override
  String toString() {
    return 'BluetoothDevice{id: $id, name: $name, type: $type, isDiscoveringServices: ${_isDiscoveringServices.value}, _services: ${_services.value}';
  }
}

enum BluetoothDeviceType { unknown, classic, le, dual }

enum BluetoothDeviceState { disconnected, connecting, connected, disconnecting }

让你的变量可以为空 蓝牙设备?设备; 稍后检查设备是否为空

The default device cannot be null because of null safety.

你搞反了。空安全永远不是某些东西可以为空或不能为空的原因。 决定某物是否可以为空。一直都是这样,程序员 决定 变量有时是否为空。所有 null-safety 所做的就是强制程序员与他们的编译器分享这个神秘的秘密知识,这样编译器就可以完成它的工作并在他们的逻辑包含错误时警告程序员。

因此,如果您认为没有用于程序启动的设备(听起来很合理),那么您可以决定使其可为空。使用 BluetoothDevice? 作为类型,它可以为空。现在你的编译器可以告诉你所有你(错误地)认为它永远不会为空的地方。这就是空安全的伟大之处。它是为了帮助您做出任何选择,而不是限制您的选择。