วิธีการนำเข้าการพึ่งพาแพลตฟอร์มเฉพาะใน Flutter / Dart (รวมเว็บกับ Android / iOS)


9

ฉันกำลังใช้shared_preferencesงานแอพพลิเคชั่น Flutter สำหรับ iOS และ Android ในเว็บฉันใช้การhttp:dartพึ่งพา ( window.localStorage) เอง เนื่องจาก Flutter สำหรับเว็บถูกรวมเข้ากับ repo ของ 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พึ่งพาในวิธีการหรือชั้นเรียนเดียวกัน ซึ่งหมายความว่าคอมไพเลอร์ไม่สามารถท่องจำการพึ่งพาเหล่านี้ได้ เป็นการดีที่การนำเข้าควรซ่อนการใช้งานเหล่านี้ ฉันจะพยายามหาตัวอย่างการนำไปปฏิบัติที่ชัดเจน
Abhilash Chandran

คุณสามารถใช้ kIsWeb บูลีนระดับโลกซึ่งสามารถบอกคุณได้ว่าแอพนั้นได้รับการรวบรวมให้ทำงานบนเว็บหรือไม่ เอกสารประกอบ: api.flutter.dev/flutter/foundation/kIsWeb-constant.html if (kIsWeb) {// ทำงานบนเว็บ! เริ่มต้นเว็บ db} else {// ใช้การตั้งค่าที่ใช้ร่วมกัน}
Shamik Chodankar

คำตอบ:


20

นี่คือแนวทางของฉันในการแก้ไขปัญหาของคุณ นี้ขึ้นอยู่กับการใช้งานจากhttpแพคเกจในขณะที่ที่นี่

แนวคิดหลักมีดังนี้

  1. สร้างคลาสนามธรรมเพื่อกำหนดวิธีการที่คุณจะต้องใช้
  2. สร้างการใช้งานเฉพาะwebและการandroidอ้างอิงซึ่งขยายคลาสนามธรรมนี้
  3. สร้าง stub ซึ่งแสดงเมธอดเพื่อส่งคืนอินสแตนซ์ของการนำนามธรรมไปใช้ นี่เป็นเพียงเพื่อให้เครื่องมือวิเคราะห์โผมีความสุข
  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 เช่นกันซึ่งเป็นเรื่องตลก :)) ขอบคุณมาก!
Giovanni

1
หวังว่านี่จะช่วยผู้อื่นเช่นกัน เราทุกคนเรียนรู้โดยการแก้ .. :-)
Abhilash Chandran

สวัสดีลองใช้รหัสของคุณแล้ว! TY จากนั้นฉันก็ค้นพบเกี่ยวกับ kIsWeb บูลีนระดับโลกซึ่งสามารถบอกคุณได้ว่าแอพนั้นได้รับการรวบรวมให้ทำงานบนเว็บหรือไม่ เอกสาร: api.flutter.dev/flutter/foundation/kIsWeb-constant.html PS- ใหม่เพื่อขอโทษก่อนล่วงหน้าหากฉันกำลังมองเห็นบางสิ่งบางอย่างจะกลายเป็นเรื่องง่ายถ้าคุณใช้งาน
Shamik Chodankar

2
@ShamikChodankar คุณพูดถูก ค่าสถานะบูลีนนี้จะเป็นประโยชน์สำหรับการตัดสินใจเชิงตรรกะบางอย่าง OP ลองใช้ตัวเลือกนี้เช่นกัน แต่ปัญหาคือถ้าเราใช้ทั้งการdart:html' and แชร์ล่วงหน้าในฟังก์ชั่นเดียวกันคอมไพเลอร์จะสร้างข้อผิดพลาดเพราะมันจะไม่รู้ว่าdart:htmlเมื่อรวบรวมกับอุปกรณ์มือถือและในทางกลับกันมันจะไม่รู้ว่าsharedpreferencesเมื่อรวบรวมกับเว็บ จัดการมันภายใน กรุณาอย่าแบ่งปันถ้าคุณมีตัวอย่างการทำงานที่ใช้ธงนี้ ฉันยังใหม่กับการกระพือ :)
Abhilash Chandran
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.