如何在 Flutter/Dart 中导入特定于平台的依赖项?(结合网页与Android/iOS) [英] How to import platform specific dependency in Flutter/Dart? (Combine Web with Android/iOS)

查看:74
本文介绍了如何在 Flutter/Dart 中导入特定于平台的依赖项?(结合网页与Android/iOS)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在适用于 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'如果 (dart.library.html) 'dart:html'if (dart.library.io) 'package:shared_preferences/shared_preferences.dart';

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

窗口窗口;类 SharedPreferences {静态未来获取 getInstance 异步 {}setString(String key, String value) {}getString(字符串键){}}类窗口{映射<字符串,字符串>本地存储;}

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

static FuturegetString(字符串键)异步{如果(kIsWeb){返回 window.localStorage[key];}SharedPreferences 首选项 = 等待 SharedPreferences.getInstance;返回首选项.getString(key);}

然而,这会产生大量错误:

lib/utils/preference_utils.dart:13:7: 错误:找不到 Getter:'窗户'.window.localStorage[key] = value;^^^^^^ lib/utils/preference_utils.dart:15:39: 错误:'Future<SharedPreferences> 类型的值Function()' 不能分配给'SharedPreferences' 类型的变量.-未来"来自dart:async".-SharedPreferences"来自package:shared_preferences/shared_preferences.dart"('../../flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.4+3/lib/shared_preferences.dart').SharedPreferences 首选项 = 等待 SharedPreferences.getInstance;^ lib/utils/preference_utils.dart:22:14: 错误:找不到 Getter:'窗户'.返回 window.localStorage[key];

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

解决方案

这是我解决您问题的方法.这是基于 http 包中的实现,如

移动

I am using shared_preferences in my Flutter application for iOS and Android. On the web I am using the http:dart dependency (window.localStorage) itself. Since Flutter for web was merged into the Flutter repo, I want to create a cross platform solution.

This means I need to import two seperate API's. This seems not to be very good supported in Dart yet, but this is what I did:

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

In my preference_utils_stub.dart file, I implemented all classes/variables which need to be visible during compile time:

Window window;

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

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

This gets rid of all errors before compilation. Now I implemented some method which checks if the application is using the web or not:

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

However, this gives loads of errors:

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

And so on. How can one use different methods/classes depending on the platform without these errors? Note that I am using more dependencies this way, not just preferences. Thanks!

解决方案

Here is my approach to your issue. This is based on the implementations from http package as in here.

The core idea is as follows.

  1. Create an abstract class to define the methods you will need to use.
  2. Create implementations specific to web and android dependencies which extends this abstract class.
  3. Create a stub which exposes a method to return the instance of this abstract implementation. This is only to keep the dart analysis tool happy.
  4. In the abstract class import this stub file along with the conditional imports specific for mobile and web. Then in its factory constructor return the instance of the specific implementation. This will be handled automatically by conditional import if written correctly.

Step-1 and 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();
}

Step-2.1: Web Key finder

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

Step-2.2: Mobile Key finder

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

Step-3:

import 'key_finder_interface.dart';

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

Then in your main.dart use the KeyFinder abstract class as if its a generic implementation. This is somewhat like an adapter pattern.

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

some screen shots

Web

mobile

这篇关于如何在 Flutter/Dart 中导入特定于平台的依赖项?(结合网页与Android/iOS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆