Flutter / Dartにプラットフォーム固有の依存関係をインポートするにはどうすればよいですか?(Android / iOSとWebを組み合わせる)


9

shared_preferencesiOSおよびAndroid用のFlutterアプリケーションで使用しています。Webでは、http:dart依存関係(window.localStorage)自体を使用しています。Flutter for webがFlutterリポジトリに統合されたので、クロスプラットフォームソリューションを作成したいと考えています。

つまり、2つの別個の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;
}

これにより、コンパイル前にすべてのエラーが取り除かれます。次に、アプリケーションがWebを使用しているかどうかをチェックするメソッドを実装しました。

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依存関係の両方を持つべきではありません。これは、コンパイラがこれらの依存関係のいずれかをツリーシェークできないことを意味します。理想的には、インポートはこれらの実装を隠す必要があります。明確な実装例を考え出します。
Abhilash Chandran

アプリがウェブ上で実行するようにコンパイルされているかどうかを通知できるグローバルブールkIsWebを使用できます。ドキュメント:api.flutter.dev/flutter/foundation/kIsWeb-constant.html if(kIsWeb){// Web上で実行されます!ウェブデータベースの初期化} else {//共有設定を使用}
Shamik Chodankar

回答:


20

これがあなたの問題に対する私のアプローチです。これはhttpここにあるように、パッケージからの実装に基づいています

基本的な考え方は次のとおりです。

  1. 使用する必要があるメソッドを定義する抽象クラスを作成します。
  2. この抽象クラスを拡張する固有の実装webandroid依存関係を作成します。
  3. この抽象実装のインスタンスを返すメソッドを公開するスタブを作成します。これはダーツ分析ツールを幸せに保つためだけのものです。
  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();
}

ステップ-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();

ステップ-3:

import 'key_finder_interface.dart';

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

次にmain.dartKeyFinder抽象クラスを汎用実装のように使用します。これは、アダプタパターンに多少似ています

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

いくつかのスクリーンショット

ウェブ ここに画像の説明を入力してください ここに画像の説明を入力してください

モバイル ここに画像の説明を入力してください


2
この多大な努力をありがとう!よくやった。その間、私は同じ方法でいました(httpパッケージも見て、これは面白いです:))。どうもありがとう!
Giovanni

1
これが他の人にも役立つことを願っています。私たちは皆、解くことによって学びます.. :-)
Abhilash Chandran

こんにちはあなたのコードがうまくいきました!ty。次に、アプリがWeb上で実行するようにコンパイルされているかどうかを確認できるグローバルブールkIsWebについて知りました。ドキュメンテーション:api.flutter.dev/flutter/foundation/kIsWeb-constant.html PS-何かを見落としている場合、事前にフラッター謝罪の新機能を使用すると、実装がはるかに簡単になる
Shamik Chodankar

2
@ShamikChodankarあなたは正しいです。このブールフラグは、特定の論理的な決定に役立ちます。OPもこのオプションを試しました。しかし、問題は、我々は両方使用した場合、あるdart:html' and 同じ機能にsharedpreferences`を、それは知っているしないので、コンパイラはエラーを生成しますdart:html、それは知らないだろう、モバイルデバイスに対してコンパイルするときと、逆にsharedpreferencesその作者ない限り、ウェブに対してコンパイルするとき内部で処理します。このフラグを利用した実用的な例がある場合は、共有してください。私も羽ばたきが初めてです:)。
Abhilash Chandran
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.