How to import platform specific dependency in Flutter/Dart? (Combine Web with Android/iOS)
Here is my approach to your issue. This is based on the implementations from http
package as in here.
The core idea is as follows.
- Create an abstract class to define the methods you will need to use.
- Create implementations specific to
web
andandroid
dependencies which extends this abstract class. - Create a stub which exposes a method to return the instance of this abstract implementation. This is only to keep the dart analysis tool happy.
- In the abstract class import this stub file along with the conditional imports specific for
mobile
andweb
. Then in its factory constructor return the instance of the specific implementation. This will be handled automatically by conditional import if written correctly.
Step-1 and 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();}
Step-2.1: Web Key finder
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();
Step-2.2: Mobile Key finder
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();
Step-3:
import 'key_finder_interface.dart';KeyFinder getKeyFinder() => throw UnsupportedError( 'Cannot create a keyfinder without the packages dart:html or package:shared_preferences');
Then in your main.dart
use the KeyFinder
abstract class as if its a generic implementation. This is somewhat like an adapter pattern.
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; }); }, ) ], ), ), ); }}
some screen shots