Làm cách nào để nhập phụ thuộc cụ thể vào nền tảng trong Flutter / Dart? (Kết hợp web với Android / iOS)


9

Tôi đang sử dụng shared_preferencestrong ứng dụng Flutter cho iOS và Android. Trên trang web tôi đang sử dụng chính http:dartphụ thuộc ( window.localStorage). Vì Flutter cho web đã được hợp nhất vào repo Flutter, tôi muốn tạo ra một giải pháp đa nền tảng.

Điều này có nghĩa là tôi cần nhập hai API riêng biệt. Điều này dường như chưa được hỗ trợ rất tốt trong Dart, nhưng đây là những gì tôi đã làm:

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

Trong preference_utils_stub.darttệp của tôi , tôi đã triển khai tất cả các lớp / biến cần hiển thị trong thời gian biên dịch:

Window window;

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

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

Điều này được loại bỏ tất cả các lỗi trước khi biên dịch. Bây giờ tôi đã triển khai một số phương pháp kiểm tra xem ứng dụng có sử dụng web hay không:

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

Tuy nhiên, điều này mang lại vô số lỗi:

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

Và như thế. Làm thế nào một người có thể sử dụng các phương thức / lớp khác nhau tùy thuộc vào nền tảng mà không có các lỗi này? Lưu ý rằng tôi đang sử dụng nhiều phụ thuộc hơn theo cách này, không chỉ là sở thích. Cảm ơn!


Trong kiến ​​thức hạn chế của tôi, bạn không nên có cả localstorageshared preferencesphụ thuộc trong cùng một phương thức hoặc lớp. Điều này có nghĩa là trình biên dịch không thể tạo ra một trong hai phụ thuộc này. Lý tưởng nhất là nhập khẩu nên ẩn những triển khai này. Tôi sẽ cố gắng đưa ra một ví dụ thực hiện rõ ràng.
Abhilash Chandran

Bạn có thể sử dụng kIsWeb boolean toàn cầu có thể cho bạn biết liệu ứng dụng có được biên dịch để chạy trên web hay không. Tài liệu: api.flutter.dev/flutter/foundation/kIsWeb-constant.html if (kIsWeb) {// đang chạy trên web! khởi tạo web db} khác {// sử dụng tùy chọn chia sẻ}
Shamik Jigankar

Câu trả lời:


20

Đây là cách tiếp cận của tôi đối với vấn đề của bạn. Điều này dựa trên việc triển khai từ httpgói như ở đây .

Ý tưởng cốt lõi là như sau.

  1. Tạo một lớp trừu tượng để xác định các phương thức bạn sẽ cần sử dụng.
  2. Tạo các triển khai cụ thể webandroidphụ thuộc mở rộng lớp trừu tượng này.
  3. Tạo một sơ khai hiển thị một phương thức để trả về thể hiện của triển khai trừu tượng này. Điều này chỉ để giữ cho công cụ phân tích phi tiêu hạnh phúc.
  4. Trong lớp trừu tượng, nhập tệp sơ khai này cùng với các nhập có điều kiện cụ thể cho mobileweb. Sau đó, trong nhà xây dựng nhà máy của nó trả về thể hiện của việc thực hiện cụ thể. Điều này sẽ được xử lý tự động bằng cách nhập có điều kiện nếu được viết chính xác.

Bước 1 và 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();
}

Bước 2.1: Công cụ tìm khóa 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();

Bước 2.2: Công cụ tìm khóa di động

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

Bước 3:

import 'key_finder_interface.dart';

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

Sau đó, trong bạn main.dartsử dụng KeyFinderlớp trừu tượng như thể nó là một triển khai chung. Điều này hơi giống như một mô hình bộ chuyển đổi .

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

một số ảnh chụp màn hình

Web nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

di động nhập mô tả hình ảnh ở đây


2
Cảm ơn vì nỗ lực rất lớn này! Làm tốt. Tôi cũng đang trong tình trạng tương tự (tìm kiếm trong gói http, thật buồn cười :)). Cảm ơn rất nhiều!
Giovanni

1
Hy vọng điều này sẽ giúp những người khác là tốt. Tất cả chúng ta đều học bằng cách giải quyết .. :-)
Abhilash Chandran

Hi đã thử mã của bạn làm việc! ty. Sau đó tôi đã tìm hiểu về kIsWeb toàn cầu có thể cho bạn biết liệu ứng dụng có được biên dịch để chạy trên web hay không. Tài liệu: api.flutter.dev/flutter/foundation/kIsWeb-constant.html PS- Mới đưa ra lời xin lỗi trước khi tôi xem xét việc triển khai sẽ trở nên đơn giản hơn rất nhiều nếu bạn sử dụng điều đó
Shamik Jigankar

2
@ShamikChodankar Bạn nói đúng. Cờ boolean này sẽ hữu ích cho quyết định hợp lý nhất định. OP cũng đã thử tùy chọn này. Nhưng vấn đề là, nếu chúng ta sử dụng cả hai dart:html' and sharedpreferences` trong cùng một chức năng, trình biên dịch sẽ tạo ra lỗi vì nó sẽ không biết dart:htmlkhi biên dịch với thiết bị di động và ngược lại, nó sẽ không biết về việc sharedpreferencesbiên dịch trên web trừ khi các tác giả của nó xử lý nó trong nội bộ. Vui lòng chia sẻ nếu bạn có một ví dụ hoạt động sử dụng cờ này. Tôi cũng mới biết rung động :).
Abhilash Chandran
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.