Bagaimana cara mengimpor ketergantungan khusus platform di Flutter / Dart? (Gabungkan Web dengan Android / iOS)

9

Saya menggunakan shared_preferencesdalam aplikasi Flutter saya untuk iOS dan Android. Di web saya menggunakan http:dartdependensi ( window.localStorage) itu sendiri. Karena Flutter untuk web digabungkan ke dalam repo Flutter, saya ingin membuat solusi lintas platform.

Ini berarti saya perlu mengimpor dua API terpisah. Ini sepertinya belum didukung dengan sangat baik di Dart, tapi inilah yang saya lakukan:

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

Dalam preference_utils_stub.dartfile saya , saya menerapkan semua kelas / variabel yang perlu terlihat selama waktu kompilasi:

Window window;

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

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

Ini menghilangkan semua kesalahan sebelum kompilasi. Sekarang saya menerapkan beberapa metode yang memeriksa apakah aplikasi menggunakan web atau tidak:

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

Namun, ini memberikan banyak kesalahan:

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

Dan seterusnya. Bagaimana seseorang dapat menggunakan metode / kelas yang berbeda tergantung pada platform tanpa kesalahan ini? Perhatikan bahwa saya menggunakan lebih banyak dependensi dengan cara ini, bukan hanya preferensi. Terima kasih!

Giovanni
sumber
Dalam pengetahuan saya yang terbatas Anda seharusnya tidak memiliki keduanya localstoragedan shared preferencesdependensi dalam metode atau kelas yang sama. Ini berarti kompilator tidak dapat mengatur salah satu dari dependensi ini. Idealnya impor harus menyembunyikan implementasi ini. Saya akan mencoba memberikan contoh implementasi yang jelas.
Abhilash Chandran
Anda bisa menggunakan global boolean kIsWeb yang dapat memberi tahu Anda apakah aplikasi dikompilasi untuk dijalankan di web atau tidak. Dokumentasi: api.flutter.dev/flutter/foundation/kIsWeb-constant.html if (kIsWeb) {// berjalan di web! inisialisasi web db} lain {// gunakan preferensi bersama}
Shamik Chodankar

Jawaban:

20

Inilah pendekatan saya untuk masalah Anda. Ini didasarkan pada implementasi dari httppaket seperti di sini .

Gagasan intinya adalah sebagai berikut.

  1. Buat kelas abstrak untuk mendefinisikan metode yang perlu Anda gunakan.
  2. Buat implementasi khusus untuk webdanandroid dependensi yang memperluas kelas abstrak ini.
  3. Buat rintisan yang memaparkan metode untuk mengembalikan instance dari implementasi abstrak ini. Ini hanya untuk membuat alat analisis panah bahagia.
  4. Di kelas abstrak, impor file rintisan ini bersama dengan impor bersyarat khusus untuk mobiledan web. Kemudian dalam konstruktor pabriknya mengembalikan instance implementasi spesifik. Ini akan ditangani secara otomatis oleh impor bersyarat jika ditulis dengan benar.

Langkah-1 dan 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();
}

Langkah-2.1: Pencari Kunci 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();

Langkah-2.2: Pencari Kunci Seluler

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

Langkah-3:

import 'key_finder_interface.dart';

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

Kemudian dalam Anda main.dartmenggunakan KeyFinderkelas abstrak seolah-olah ini merupakan implementasi generik. Ini agak seperti pola adaptor .

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

beberapa tangkapan layar

Web masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

ponsel masukkan deskripsi gambar di sini

Abhilash Chandran
sumber
2
Terima kasih atas upaya besar ini! Sudah selesai dilakukan dengan baik. Sementara itu saya berada di jalan yang sama (melihat dalam paket http juga, yang lucu :)). Terima kasih banyak!
Giovanni
1
Semoga ini bisa membantu orang lain juga. Kita semua belajar dengan memecahkan .. :-)
Abhilash Chandran
Hai, coba kode Anda berhasil! ty. Saya kemudian mengetahui tentang kIsWeb boolean global yang dapat memberi tahu Anda apakah aplikasi dikompilasi untuk dijalankan di web atau tidak. Dokumentasi: api.flutter.dev/flutter/foundation/kIsWeb-constant.html PS- Baru untuk mengibaskan permintaan maaf sebelumnya jika saya melihat implementasi sesuatu menjadi jauh lebih mudah jika Anda menggunakannya
Shamik Chodankar
2
@ShamikChodankar Anda benar. Bendera boolean ini akan membantu untuk keputusan logis tertentu. OP juga mencoba opsi ini. Tetapi masalahnya adalah, jika kita menggunakan kedua dart:html' and sharedpreferences` dalam fungsi yang sama, kompiler akan menghasilkan kesalahan karena ia tidak akan tahu tentang dart:htmlkapan kompilasi terhadap perangkat seluler dan sebaliknya itu tidak akan tahu tentang sharedpreferenceskapan kompilasi dengan web kecuali pembuatnya menanganinya secara internal. Silakan bagikan jika Anda memiliki contoh kerja menggunakan bendera ini. Saya juga baru mengepak :).
Abhilash Chandran