我正在尝试让我的flutter应用程序在浏览器中运行,这取决于firebase_database。确实没有任何有关此操作的文档,但是我根据firebase_core和firebase_auth文档进行了一些假设:
https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_core/firebase_core_web
https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_auth/firebase_auth_web
我的应用程序可以在iOS和android上运行,但是我无法使数据库在波动的网络中正常工作。
我已经设置了index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flutter WebRTC Demo</title>
</head>
<body>
<script src="https://www.gstatic.com/firebasejs/7.6.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.6.1/firebase-database.js"></script>
<script>
const firebaseConfig = {
apiKey: '...',
authDomain: '...',
databaseURL: '...',
projectId: '...',
storageBucket: '...',
messagingSenderId: '...',
appId: '...'
};
firebase.initializeApp(firebaseConfig);
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
但是,当我尝试使用Firebase数据库时,日志中出现错误:
MissingPluginException(No implementation found for method DatabaseReference#set on channel plugins.flutter.io/firebase_database)
package:dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 196:49 throw_
package:flutter/src/services/platform_channel.dart 319:7 invokeMethod
package:dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 47:50 onValue
package:dart-sdk/lib/async/zone.dart 1381:54 runUnary
package:dart-sdk/lib/async/future_impl.dart 139:18 handleValue
package:dart-sdk/lib/async/future_impl.dart 680:44 handleValueCallback
package:dart-sdk/lib/async/future_impl.dart 709:32 _propagateToListeners
package:dart-sdk/lib/async/future_impl.dart 524:5 [_completeWithValue]
package:dart-sdk/lib/async/future_impl.dart 554:7 callback
package:dart-sdk/lib/async/schedule_microtask.dart 43:11 _microtaskLoop
package:dart-sdk/lib/async/schedule_microtask.dart 52:5 _startMicrotaskLoop
package:dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 168:15 <fn>
有什么办法可以使实时数据库在Web上的Flutter应用中正常工作?
答案 0 :(得分:12)
在the main README in the flutterfire github中,有一个“ Web”?列,以说明哪些插件可用于网络。
当前,浮动Web应用程序仅支持firebase_core
,firebase_auth
,cloud_firestore
和firebase_functions
。
如@Frank van Puffelen所述,要在flutter网站中使用firebase的全部功能,请使用firebase-dart包装器库。
还有一个Flutter Web Plugins Project Board,显示了路线图上哪些flutter插件以及它们处于开发的哪个阶段。在进行此编辑时,firebase_storage
是网络路线图上的下一个插件。
答案 1 :(得分:5)
FlutterFire插件最初是为在iOS和Android的本机移动应用程序中运行而构建的。正如我们所说的那样,对Web平台的支持正在添加到插件中,但是涵盖所有Firebase产品还需要一些时间。
您可以在Github存储库上的available FlutterFire plugins列表中检查当前与Flutter for web兼容的模块。
要在Flutter中将Firebase用于其他功能的网络,请使用firebase-dart
plugin。这意味着您将需要针对Web和移动设备使用单独的代码,但是您可以仅在应用程序的一小部分中就可以区分出差异。
答案 2 :(得分:2)
我遇到了同样的问题,因此决定对此做些事情。所以我继续做https://pub.dev/packages/firebase_db_web_unofficial。设置并集成到您的代码中很容易。
答案 3 :(得分:2)
以防万一有人仍在寻找 Flutter Web 实时数据库问题的另一种解决方法,我有一个简单而直接的解决方案......
我做了一些挖掘,if(kIsWeb) 似乎有效。
第一
添加支持网络实时数据库的firebase package和安卓|ios的firebase_databe package。
第二
初始化firebase
void main() async {
await Firebase.initializeApp();
}
第三
导入如下
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase/firebase.dart' as fb;
第四
关于如何读取 android-ios / web 实时数据库数据的示例。 在这里,我将图像添加到轮播滑块。
List<SliderImage> sliderList = [];
void getSliderData() async {
FirebaseDatabase firebaseDatabaseference = FirebaseDatabase.instance;
firebaseDatabaseference.setPersistenceEnabled(true);
firebaseDatabaseference.setPersistenceCacheSizeBytes(10000000);
//for web
if (kIsWeb) {
fb.DatabaseReference databaseRef = fb.database().ref("Slider");
await databaseRef.onValue.listen((event) {
fb.DataSnapshot dataSnapshot = event.snapshot;
sliderList.clear();
this.setState(() {
for (var value in dataSnapshot.val()) {
sliderList.add(new SliderImage.fromJson(value));
}
});
});
// for android and ios
} else {
DatabaseReference databaseReference = firebaseDatabaseference.reference();
databaseReference.keepSynced(true);
await databaseReference
.child("Slider")
.once()
.then((DataSnapshot dataSnapshot) {
sliderList.clear();
this.setState(() {
for (var value in dataSnapshot.value) {
sliderList.add(new SliderImage.fromJson(value));
}
});
});
}
}
轮播滑块
CarouselSlider.builder(
itemCount: sliderList.length,
options: CarouselOptions(
autoPlay: true,
aspectRatio: 16 / 9,
viewportFraction: 1,
enlargeCenterPage: false,
enlargeStrategy: CenterPageEnlargeStrategy.height,
),
itemBuilder: (context, index, realIdx) {
return Container(
child: Center(
child: Image.network(sliderList[index].image, loadingBuilder:
(BuildContext context, Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
valueColor:
new AlwaysStoppedAnimation<Color>(Colors.black54),
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes
: null,
),
);
}, fit: BoxFit.cover, width: 1000)),
);
},
));
SliderImage 模型类
class SliderImage {
String image;
SliderImage(this.image);
SliderImage.fromJson(var value) {
this.image = value['image'];
}
}
类似的方法可以应用于 Listview.builder。 干杯????????
答案 4 :(得分:0)
在这个问题上经过几天的努力之后,由于评论中未解决的问题很少,我决定发布一个完整的,长篇幅的答案,以帮助人们从我本人的角度出发。
这就是我实现两个不同程序包的方式。
当我使用flutter_bloc
进行状态管理时,我基本上必须像对待用户位置那样使存储库平台依赖。
为此,我使用了存根/抽象类/ Web实现/设备实现模式。因此,在我的块存储库中,我只调用抽象类方法,这些方法将使用适当的包映射到适当的平台实现类。乍一看似乎有些混乱,但是一旦掌握了这个概念就很容易了,但是Thera是一些陷阱,在开始使用模式时可能会陷入其中。
对于设备实现,使用flutter_auth
软件包,而对于Web实现,使用flutter
软件包,为了简化起见,我制作了一个单例。现在,单例将返回初始化的Firebase App
,使您可以访问所有服务。auth()
,database()
,firestore()`,remoteconfig()...
只要您需要在任何地方访问任何Firebase服务,就可以实例化Firebase并使用这些服务。
App firebase = FirebaseWeb.instance.app;
...
await firebase.auth().signInWithCredential(credential);
return firebase.auth().currentUser;
这是我用于授权的所有代码,但是很容易适应不同的firebase服务:
这只是为了保存一个(getter)方法,该方法在抽象类工厂方法(我称为switcher)中返回,并允许将抽象类中的条件导入到正确的实现类。
import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_switcher.dart';
UserRepositorySwitcher getUserRepository() {
print('user_repository_stub called');
}
在这里导入存根,以便能够有条件地导入适当的实现类。类工厂方法中返回的存根(getter)方法。 在此类中,您需要声明所有需要使用的方法。这里的回报是动态的,因为特定于软件包的回报将在平台实现类中。 请注意在有条件导入中输入错误和正确的文件路径,因为没有自动检查。.花了我很多时间来找出它。.
import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_stub.dart'
if (dart.library.io) 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_device.dart'
if (dart.library.js) 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_web.dart';
abstract class UserRepositorySwitcher {
Future<dynamic> signInWithGoogle() async {
print('UserREpository switcher signInWithGoogle() called');
}
Future<void> signInWithCredential({String email, String password}) {}
Future<void> signUp({String email, String password}) {}
Future<void> signOut() async {}
Future<bool> isSignedIn() async {}
Future<dynamic> getUser() async {}
factory UserRepositorySwitcher() => getUserRepository();
}
必须实现抽象类以使用特定的方法和类型(在这种情况下为flutter_auth
)来实现并实现其方法。在这里,您还必须在类范围之外声明存根中返回设备实现类的相同方法(请参见底部代码)。
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebaseblocwebstub/authentication_bloc/app_user.dart';
import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_switcher.dart';
import 'package:google_sign_in/google_sign_in.dart';
class UserRepositoryDevice implements UserRepositorySwitcher {
final FirebaseAuth _firebaseAuth;
final GoogleSignIn _googleSignIn;
UserRepositoryDevice({FirebaseAuth firebaseAuth, GoogleSignIn googleSignIn})
: _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance,
_googleSignIn = googleSignIn ?? GoogleSignIn();
Future<FirebaseUser> signInWithGoogle() async {
print('signInWithGoogle() from device started');
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
print('GoogleUser is : $googleUser');
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = await GoogleAuthProvider.getCredential(
idToken: googleAuth.idToken, accessToken: googleAuth.accessToken);
await _firebaseAuth.signInWithCredential(credential);
return _firebaseAuth.currentUser();
}
Future<void> signInWithCredential({String email, String password}) {
return _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
}
Future<void> signUp({String email, String password}) {
return _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
}
Future<void> signOut() async {
return Future.wait([
_firebaseAuth.signOut(),
_googleSignIn.signOut(),
]);
}
Future<bool> isSignedIn() async {
final currentUser = _firebaseAuth.currentUser();
return currentUser != null;
}
Future<FixitUser> getUser() async {
String displayName = (await _firebaseAuth.currentUser()).displayName;
String email = (await _firebaseAuth.currentUser()).email;
String uid = (await _firebaseAuth.currentUser()).uid;
String photoUrl = (await _firebaseAuth.currentUser()).photoUrl;
String phoneNumber = (await _firebaseAuth.currentUser()).phoneNumber;
FixitUser user = FixitUser(
// fixitUser
name: displayName ?? '',
email: email,
phoneNumber: phoneNumber ?? '',
uid: uid,
photoUrl: photoUrl ?? '');
return (user);
}
}
UserRepositorySwitcher getUserRepository() => UserRepositoryDevice();
现在终于可以上网了。
为了轻松使用firebase
包,我决定将其设置为单例。
在这里,您可以返回一个Future<App>
实例,但是随后您必须.then
进行所有操作。或者直接返回App
。.我选择了这种方式。
这样,您就不必在index.html
文件中初始化firebase,否则您将得到一个错误,因为它已经被初始化。在此处初始化firebase也会使您的密钥不暴露。.
import 'dart:async';
import 'package:firebase/firebase.dart';
class FirebaseWeb {
// Singleton instance
static final FirebaseWeb _singleton = FirebaseWeb._();
// Singleton accessor
static FirebaseWeb get instance => _singleton;
// A private constructor. Allows us to create instances of AppDatabase
// only from within the AppDatabase class itself.
FirebaseWeb._();
static App _app;
// Database object accessor
App get app {
print('firebase get app called ');
print('_app is $_app');
if (_app != null) {
return _app;
} else {
print('initialize app');
_app = initializeApp(
apiKey: "your key",
authDomain: "your key",
databaseURL: "your key",
projectId: "your key",
storageBucket: "your key",
messagingSenderId: "your key",
appId: "your key");
print('initialized app is $_app'); // await _initializeApp();
return _app;
}
}
}
在这里,您仅使用单例实例化Firebase,并实现抽象类方法,使用其服务和方法。我在这里使用auth()
。
您可以看到(注释掉的部分)如果在单例中返回Future<App>
,则实现的详细程度要高得多。
在这里,存根getter方法将返回此类..(检查底部)
import 'dart:async';
import 'package:firebase/firebase.dart';
import 'package:firebaseblocwebstub/authentication_bloc/app_user.dart';
import 'package:firebaseblocwebstub/firebase_singleton.dart';
import 'package:firebaseblocwebstub/platform_user_repository/platform_user_repository_switcher.dart';
import 'package:google_sign_in/google_sign_in.dart';
class UserRepositoryWeb implements UserRepositorySwitcher {
App firebase = FirebaseWeb.instance.app;
final GoogleSignIn _googleSignIn = GoogleSignIn();
Future<User> signInWithGoogle() async {
print('signInWithGoogle() started');
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
print('GoogleUser is : $googleUser');
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final OAuthCredential credential = await GoogleAuthProvider.credential(
googleAuth.idToken, googleAuth.accessToken);
// singleton retunrning Future<App>
// await firebase.then((firebase) {
// firebase.auth().signInWithCredential(credential);
// return;
// });
// return firebase.then((firebase) {
// return firebase.auth().currentUser;
// });
await firebase.auth().signInWithCredential(credential);
return firebase.auth().currentUser;
}
Future<void> signInWithCredential({String email, String password}) {
return firebase.auth().signInWithEmailAndPassword(email, password);
// singleton retunrning Future<App>
// return firebase.then((firebase) {
// return firebase.auth().signInWithEmailAndPassword(email, password);
// });
}
Future<void> signUp({String email, String password}) {
return firebase.auth().createUserWithEmailAndPassword(email, password);
// singleton retunrning Future<App>
// return firebase.then((firebase) {
// return firebase.auth().createUserWithEmailAndPassword(email, password);
// });
}
Future<void> signOut() async {
return Future.wait([
firebase.auth().signOut(),
// singleton retunrning Future<App>
// firebase.then((firebase) {
// firebase.auth().signOut();
// }),
_googleSignIn.signOut(),
]);
}
Future<bool> isSignedIn() async {
final currentUser = firebase.auth().currentUser;
return currentUser != null;
// singleton retunrning Future<App>
// User firebaseUser = firebase.then((firebase) {
// return firebase.auth().currentUser;
// }) as User;
// return firebaseUser != null;
}
Future<FixitUser> getUser() async {
// singleton retunrning Future<App>
// User firebaseUser = firebase.then((firebase) {
// return firebase.auth().currentUser;
// }) as User;
//
// FixitUser user = FixitUser(
// name: firebaseUser.displayName ?? '',
// email: firebaseUser.email,
// phoneNumber: firebaseUser.phoneNumber ?? '',
// uid: firebaseUser.uid,
// photoUrl: firebaseUser.photoURL ?? '');
// return (user);
// }
String displayName = (firebase.auth().currentUser).displayName;
String email = (firebase.auth().currentUser).email;
String uid = (firebase.auth().currentUser).uid;
String photoUrl = (firebase.auth().currentUser).photoURL;
String phoneNumber = (firebase.auth().currentUser).phoneNumber;
FixitUser user = FixitUser(
name: displayName ?? '',
email: email,
phoneNumber: phoneNumber ?? '',
uid: uid,
photoUrl: photoUrl ?? '');
return (user);
}
}
UserRepositorySwitcher getUserRepository() => UserRepositoryWeb();