如何在 Flutter/Dart 中导入特定于平台的依赖项?(结合网络与Android/iOS)

Gio*_*gen 41 dart flutter flutter-web

shared_preferences在适用于 iOS 和 Android 的 Flutter 应用程序中使用。在网络上,我使用http:dart依赖项 ( window.localStorage) 本身。由于 Flutter for web 被合并到 Flutter repo 中,我想创建一个跨平台的解决方案。

这意味着我需要导入两个单独的 API。这在 Dart 中似乎还没有得到很好的支持,但这就是我所做的:

import 'package:some_project/stub/preference_utils_stub.dart'
    if (dart.library.html) 'dart:html'
    if (dart.library.io) 'package:shared_preferences/shared_preferences.dart';

Run Code Online (Sandbox Code Playgroud)

在我的preference_utils_stub.dart文件中,我实现了在编译时需要可见的所有类/变量:

Window window;

class SharedPreferences {
  static Future<SharedPreferences> get getInstance async {}
  setString(String key, String value) {}
  getString(String key) {}
}

class Window {
  Map<String, String> localStorage;
}

Run Code Online (Sandbox Code Playgroud)

这在编译之前消除了所有错误。现在我实现了一些检查应用程序是否正在使用网络的方法:

static Future<String> getString(String key) async {
    if (kIsWeb) {
       return window.localStorage[key];
    }
    SharedPreferences preferences = await SharedPreferences.getInstance;
    return preferences.getString(key);
}
Run Code Online (Sandbox Code Playgroud)

但是,这会产生大量错误:

lib/utils/preference_utils.dart:13:7: Error: Getter not found:
'window'.
      window.localStorage[key] = value;
      ^^^^^^ lib/utils/preference_utils.dart:15:39: Error: A value of type 'Future<SharedPreferences> Function()' can't be assigned to a
variable of type 'SharedPreferences'.
 - 'Future' is from 'dart:async'.
 - 'SharedPreferences' is from 'package:shared_preferences/shared_preferences.dart'
('../../flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.4+3/lib/shared_preferences.dart').
      SharedPreferences preferences = await SharedPreferences.getInstance;
                                      ^ lib/utils/preference_utils.dart:22:14: Error: Getter not found:
'window'.
      return window.localStorage[key];
Run Code Online (Sandbox Code Playgroud)

等等。如何在没有这些错误的情况下根据平台使用不同的方法/类?请注意,我以这种方式使用了更多依赖项,而不仅仅是首选项。谢谢!

Abh*_*ran 79

这是我对您的问题的处理方法。这是基于http包中的实现,如here

核心思想如下。

  1. 创建一个抽象类来定义您需要使用的方法。
  2. 创建扩展此抽象类的特定于webandroid依赖项的实现。
  3. 创建一个存根,它公开一个方法来返回这个抽象实现的实例。这只是为了让 dart 分析工具满意。
  4. 在抽象类中导入此存根文件以及特定于mobile和的条件导入web。然后在其工厂构造函数中返回具体实现的实例。如果写入正确,这将通过条件导入自动处理。

步骤 1 和 4:

import 'key_finder_stub.dart'
    // ignore: uri_does_not_exist
    if (dart.library.io) 'package:flutter_conditional_dependencies_example/storage/shared_pref_key_finder.dart'
    // ignore: uri_does_not_exist
    if (dart.library.html) 'package:flutter_conditional_dependencies_example/storage/web_key_finder.dart';

abstract class KeyFinder {

  // some generic methods to be exposed.

  /// returns a value based on the key
  String getKeyValue(String key) {
    return "I am from the interface";
  }

  /// stores a key value pair in the respective storage.
  void setKeyValue(String key, String value) {}

  /// factory constructor to return the correct implementation.
  factory KeyFinder() => getKeyFinder();
}
Run Code Online (Sandbox Code Playgroud)

步骤 2.1:Web 密钥查找器

import 'dart:html';

import 'package:flutter_conditional_dependencies_example/storage/key_finder_interface.dart';

Window windowLoc;

class WebKeyFinder implements KeyFinder {

  WebKeyFinder() {
    windowLoc = window;
    print("Widnow is initialized");
    // storing something initially just to make sure it works. :)
    windowLoc.localStorage["MyKey"] = "I am from web local storage";
  }

  String getKeyValue(String key) {
    return windowLoc.localStorage[key];
  }

  void setKeyValue(String key, String value) {
    windowLoc.localStorage[key] = value;
  }  
}

KeyFinder getKeyFinder() => WebKeyFinder();
Run Code Online (Sandbox Code Playgroud)

步骤 2.2:移动钥匙查找器

import 'package:flutter_conditional_dependencies_example/storage/key_finder_interface.dart';
import 'package:shared_preferences/shared_preferences.dart';

class SharedPrefKeyFinder implements KeyFinder {
  SharedPreferences _instance;

  SharedPrefKeyFinder() {
    SharedPreferences.getInstance().then((SharedPreferences instance) {
      _instance = instance;
      // Just initializing something so that it can be fetched.
      _instance.setString("MyKey", "I am from Shared Preference");
    });
  }

  String getKeyValue(String key) {
    return _instance?.getString(key) ??
        'shared preference is not yet initialized';
  }

  void setKeyValue(String key, String value) {
    _instance?.setString(key, value);
  }

}

KeyFinder getKeyFinder() => SharedPrefKeyFinder();
Run Code Online (Sandbox Code Playgroud)

第 3 步:

import 'key_finder_interface.dart';

KeyFinder getKeyFinder() => throw UnsupportedError(
    'Cannot create a keyfinder without the packages dart:html or package:shared_preferences');
Run Code Online (Sandbox Code Playgroud)

然后在您main.dart使用KeyFinder抽象类时,就好像它是一个通用实现一样。这有点像适配器模式

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_conditional_dependencies_example/storage/key_finder_interface.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    KeyFinder keyFinder = KeyFinder();
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SafeArea(
        child: KeyValueWidget(
          keyFinder: keyFinder,
        ),
      ),
    );
  }
}

class KeyValueWidget extends StatefulWidget {
  final KeyFinder keyFinder;

  KeyValueWidget({this.keyFinder});
  @override
  _KeyValueWidgetState createState() => _KeyValueWidgetState();
}

class _KeyValueWidgetState extends State<KeyValueWidget> {
  String key = "MyKey";
  TextEditingController _keyTextController = TextEditingController();
  TextEditingController _valueTextController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Material(
      child: Container(
        width: 200.0,
        child: Column(
          children: <Widget>[
            Expanded(
              child: Text(
                '$key / ${widget.keyFinder.getKeyValue(key)}',
                style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
              ),
            ),
            Expanded(
              child: TextFormField(
                decoration: InputDecoration(
                  labelText: "Key",
                  border: OutlineInputBorder(),
                ),
                controller: _keyTextController,
              ),
            ),
            Expanded(
              child: TextFormField(
                decoration: InputDecoration(
                  labelText: "Value",
                  border: OutlineInputBorder(),
                ),
                controller: _valueTextController,
              ),
            ),
            RaisedButton(
              child: Text('Save new Key/Value Pair'),
              onPressed: () {
                widget.keyFinder.setKeyValue(
                  _keyTextController.text,
                  _valueTextController.text,
                );
                setState(() {
                  key = _keyTextController.text;
                });
              },
            )
          ],
        ),
      ),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

一些屏幕截图

网络 在此处输入图片说明 在此处输入图片说明

移动的 在此处输入图片说明

  • 我发现 dart 编译器没有导入(不是 Web,也不是本机),所以当我编译时,我得到“找不到方法 getKeyFinder()” 知道这是否仍然适用于 dart 2.0?@阿比拉什钱德兰 (6认同)
  • 感谢您付出的巨大努力!做得好。与此同时,我也以同样的方式(也在 http 包中查找,这很有趣:))。多谢! (3认同)
  • @ShamikChodankar 你是对的。这个布尔标志将有助于某些逻辑决策。OP也尝试过这个选项。但问题是,如果我们在同一个函数中同时使用“dart:html”和“sharedpreferences”,编译器将生成错误,因为在针对移动设备进行编译时它不会知道“dart:html”,相反它会生成错误。在针对网络进行编译时不会知道“sharedpreferences”,除非其作者在内部处理它。如果您有使用此标志的工作示例,请分享。我也是颤振的新手:)。 (3认同)
  • 回答@perrohunter,将 getKeyFinder() 方法保留在类之外。仔细观察 AbhilashChandran 类“WebKeyFinder”,您会发现我的建议。感谢您的回答,这解决了我的网络特定的“dart.html”包问题。 (3认同)
  • 希望这也对其他人有帮助。我们都通过解决问题来学习..:-) (2认同)
  • 感谢您提供的帮助,对于那些遇到“找不到方法”错误的人,实现中的“getKeyFinder()”方法(“SharedPrefKeyFinder”和“WebKeyFinder”)应该位于实现类之外。 (2认同)