Flutter / Dart에서 플랫폼 별 종속성을 가져 오는 방법은 무엇입니까? (Android / iOS와 웹 결합)


9

shared_preferencesiOS 및 Android 용 Flutter 응용 프로그램에서 사용 하고 있습니다. 웹에서 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];

등등. 이러한 오류없이 플랫폼에 따라 다른 방법 / 클래스를 어떻게 사용할 수 있습니까? 선호도뿐만 아니라이 방법으로 더 많은 종속성을 사용하고 있습니다. 감사!


제한된 지식 으로는 동일한 메소드 또는 클래스에 의존성 localstorageshared preferences의존성 이 없어야합니다 . 이는 컴파일러가 이러한 종속성 중 하나를 트리 쉐이킹 할 수 없음을 의미합니다. 이상적으로 가져 오기는 이러한 구현을 숨겨야합니다. 명확한 구현 예를 생각해 보겠습니다.
Abhilash Chandran

앱이 웹에서 실행되도록 컴파일되었는지 여부를 알려주는 전역 부울 kIsWeb을 사용할 수 있습니다. 문서 : api.flutter.dev/flutter/foundation/kIsWeb-constant.html if (kIsWeb) {// 웹에서 실행 중! 초기화 웹 DB} 다른 {// 사용 공유 환경 설정}
Shamik Chodankar

답변:


20

귀하의 문제에 대한 나의 접근 방식은 다음과 같습니다. 이것은 여기 에서 http와 같이 패키지 의 구현을 기반으로합니다 .

핵심 아이디어는 다음과 같습니다.

  1. 사용해야 할 메소드를 정의하기 위해 추상 클래스를 작성하십시오.
  2. web및 특정 구현 구현android이 추상 클래스를 확장 종속 .
  3. 이 추상 구현의 인스턴스를 리턴하는 메소드를 공개하는 스텁을 작성하십시오. 이것은 다트 분석 도구를 만족시키기위한 것입니다.
  4. 추상 클래스에서 조건부 수입에 대한 구체적인와 함께이 스텁 파일을 가져 mobileweb. 그런 다음 팩토리 생성자에서 특정 구현의 인스턴스를 반환합니다. 올바르게 작성된 경우 조건부 가져 오기에 의해 자동으로 처리됩니다.

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-2 단계 : 웹 키 파인더

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 단계 : 모바일 키 파인더

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

안녕하세요 코드가 작동했습니다! 타이. 그런 다음 앱이 웹에서 실행되도록 컴파일되었는지 여부를 알 수있는 전역 부울 kIsWeb에 대해 알았습니다. 문서 : api.flutter.dev/flutter/foundation/kIsWeb-constant.html PS- 당신이 그것을 사용하면 구현이 훨씬 간단 해지 기 전에 미리 사과를 펄럭입니다.
처음 접하는

2
@ShamikChodankar 당신이 맞아요. 이 부울 플래그는 특정 논리적 결정에 도움이됩니다. OP는이 옵션도 시도했습니다. 그러나 문제는 dart:html' and 동일한 기능에서 공유 환경 설정을 모두 사용 하면 컴파일러는 dart:html모바일 장치에 대해 sharedpreferences컴파일 할 때를 알지 못하고 반대로 저자가 아닌 한 웹을 컴파일 할 때 알지 못하기 때문에 오류를 생성합니다 내부적으로 처리하십시오. 이 플래그를 사용하는 실제 예제가 있으면 공유하십시오. 나는 또한 설레다 :).
Abhilash Chandran
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.