【问题标题】:如何在 Flutter/Dart 中导入特定于平台的依赖项? (结合 Web 与 Android/iOS)
【发布时间】:2020-03-01 18:16:00
【问题描述】:

我在 iOS 和 Android 的 Flutter 应用程序中使用 shared_preferences。在网络上,我正在使用 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';

在我的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;
}

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

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

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

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];

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

【问题讨论】:

  • 在我有限的知识中,您不应该在同一方法或类中同时拥有 localstorageshared preferences 依赖项。这意味着编译器无法对这些依赖项中的任何一个进行树摇。理想情况下,导入应该隐藏这些实现。我将尝试提出一个清晰的实现示例。
  • 您可以使用全局布尔 kIsWeb,它可以告诉您应用程序是否已编译为在 Web 上运行。文档:api.flutter.dev/flutter/foundation/kIsWeb-constant.html if (kIsWeb) { // 在网络上运行!初始化网络数据库 }else{ // 使用共享首选项 }

标签: flutter dart flutter-web


【解决方案1】:

你可以使用包universal_html

【讨论】:

    【解决方案2】:

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

    核心思想如下。

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

    第 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();
    }
    

    步骤 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();
    

    步骤 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();
    

    第三步:

    import 'key_finder_interface.dart';
    
    KeyFinder getKeyFinder() => throw UnsupportedError(
        'Cannot create a keyfinder without the packages dart:html or package:shared_preferences');
    

    然后在您的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;
                    });
                  },
                )
              ],
            ),
          ),
        );
      }
    }
    
    

    一些屏幕截图

    网络

    移动

    【讨论】:

    • 感谢您付出的巨大努力!做得好。与此同时,我也在以同样的方式(也在查看 http 包,这很有趣:))。非常感谢!
    • 希望这对其他人也有帮助。我们都通过解决来学习.. :-)
    • @ShamikChodankar 你是对的。这个布尔标志将有助于某些逻辑决策。 OP 也尝试了这个选项。但问题是,如果我们在同一个函数中同时使用dart:html' and sharedpreferences`,编译器会产生错误,因为在针对移动设备编译时它不会知道dart:html,相反它不会知道@ 987654343@ 在针对 web 进行编译时,除非其作者在内部处理它。如果您有使用此标志的工作示例,请分享。我也是 Flutter 的新手:)。
    • 我看到 dart 编译器也没有导入(不是 web,也不是本机),所以当我编译时,我得到 method not found getKeyFinder() 知道这是否仍在 dart 2.0 上工作? @AbhilashChandran
    • 回答@perrohunter,将您的 getKeyFinder() 方法保留在课堂之外。仔细观察你会发现的 AbhilashChandran 类“WebKeyFinder”,这是我的建议。感谢您的回答,这解决了我的网络特定的“dart.html”包问题。
    猜你喜欢
    • 2020-06-05
    • 2020-02-04
    • 2019-12-12
    • 1970-01-01
    • 1970-01-01
    • 2013-04-09
    • 2014-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多