Як імпортувати залежність від платформи у Flutter / Dart? (Поєднуйте Інтернет з Android / iOS)


9

Я використовую shared_preferencesв моєму додатку Flutter для iOS та Android. В Інтернеті я використовую саму http:dartзалежність ( window.localStorage). Оскільки Flutter для Інтернету було об'єднано в репортаж Flutter, я хочу створити кросплатформенне рішення.

Це означає, що мені потрібно імпортувати два окремих 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];

І так далі. Як можна використовувати різні методи / класи залежно від платформи без цих помилок? Зауважте, що я використовую більше залежностей таким чином, а не лише уподобаннями. Дякую!


З моїх обмежених знань, у вас не повинно бути localstorageі shared preferencesзалежності, і залежності в одному методі чи класі. Це означає, що компілятор не може вирішити жодну з цих залежностей. В ідеалі імпорт повинен приховувати ці реалізації. Я спробую придумати чіткий приклад реалізації.
Абхілаш Чандран

Ви можете використовувати глобальний логічний kIsWeb, який може повідомити вам, чи було додаток складено для запуску в Інтернеті. Документація: api.flutter.dev/flutter/foundation/kIsWeb-constant.html if (kIsWeb) {// працює в Інтернеті! ініціалізувати web db} else {// використовувати спільні уподобання}
Shamik Chodankar

Відповіді:


20

Ось мій підхід до вашого питання. Це засновано на реалізаціях з httpпакету, як тут .

Основна ідея полягає в наступному.

  1. Створіть абстрактний клас, щоб визначити методи, які вам потрібно буде використовувати.
  2. Створіть конкретні webта androidзалежності, які розширюють цей абстрактний клас.
  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: Пошук веб-ключів

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

деякі знімки екрана

Веб введіть тут опис зображення введіть тут опис зображення

мобільний введіть тут опис зображення


2
Дякую за це величезне зусилля! Молодці. Я тим часом був таким же чином (дивлячись і в http-пакет, що смішно :)). Дуже дякую!
Джованні

1
Сподіваюся, що це допомагає і іншим. Ми всі
вчимось,

Привіт, спробував ваш код спрацював! ти. Тоді я дізнався про глобальний логічний kIsWeb, який може повідомити вам, чи додаток було складено для запуску в Інтернеті. Документація: api.flutter.dev/flutter/foundation/kIsWeb-constant.html PS- Нове, щоб заздалегідь заграти вибачення, якщо я не помічаю, що реалізація стане набагато простішою, якщо ви користуєтесь цим
Шамік Чоданкар

2
@ShamikChodankar Ви праві. Цей булевий прапор буде корисним для певного логічного рішення. ОП також спробував цей варіант. Але проблема полягає в тому, що якщо ми використовуємо обидві dart:html' and спільні налаштування 'в одній і тій же функції, компілятор буде генерувати помилки, оскільки він не буде знати dart:htmlпри компіляції з мобільного пристрою, і навпаки, не буде знати про те, sharedpreferencesколи збирається в Інтернеті, якщо його автори не будуть обробляти це внутрішньо. Будь ласка, поділіться, якщо у вас є робочий приклад використання цього прапора. Я теж новачок хитаюся :).
Абхілаш Чандран
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.