应用 Flutter 和 Firebase 制作!计数器应用程序
目录
- Flutter 概述和特点
- Firebase 概览和服务列表
- 开发环境
- 筹备编码
- Firebase Analytics编
- Firebase Crashlytics编
- Firebase Remote Config
- Firebase Authentication
- Cloud Firestore
- Firebase Realtime Database
- Cloud Storage for Firebase
- Firebase Cloud Messaging
- Firebase In-App Messaging
- Firebase ML
- Cloud Functions for Firebase
- Firebase Hosting
- Firebase Performance Monitoring
- Firebase 其余服务
- Firebase Authentication
- 参考网站
- 参考网站
<div id="01">1️Flutter 概述和特点</div>
什么是Flutter?
Flutter是一个由谷歌开发的开源应用程序框架。
Flutter官网
Flutter的特点
- <span>只需一段代码就能为多个平台创立应用程序,包含Android, iOS, Web, Windows, MacOS和Linux。</span>
- <span>轻松拜访 Material Design</span>
- <span>UI是应用小工具的组合来构建的</span>
- <span>应用Dart作为开发语言</span>
- <span>热重载性能实现了疾速开发</span>
Flutter的文档
Flutter/Dart
有一套残缺的官网文档。
这里有一些例子
对于如何开发Flutter应用程序的文档。
Flutter官网文档
Flutter API参考
FlutterAPI参考
Dart 包搜寻站点
Dart 包搜寻站点
此外,Flutter一年比一年受欢迎,除了官网文档外,许多开发者在其余网站上整顿了一些通俗易懂的文章,能够作为开发的参考。
<div id="02">2️Firebase 概览和服务列表</div>
什么是Firebase?
Firebase是谷歌提供的一个挪动后盾服务(mBaaS)。
Firebase能够很容易地将数据存储和通过云同步、利用认证、音讯告诉、利用剖析和性能测量等性能增加到挪动利用。
Firebase 服务列表
名称 | 内容 |
---|---|
A/B Testing | 轻松运行并剖析产品和营销测试 |
Analytics | 利用剖析性能 |
App Check | 为应用程序数据提供爱护 |
App Distribution | 将应用程序散发到测试人员 |
Firebase Authentication | 易于建设的用户认证 |
Cloud Firestore | NoSQL 数据库构建无服务器 |
Cloud Functions for Firebase | 无服务器运行后端代码 |
Firebase Cloud Messaging | 发送和接管推送音讯 |
Firebase Crashlytics | 跟踪利用稳定性问题 |
Dynamic Links | 提供对本机应用程序链接内容的间接导航 |
Firebase Extensions | Firebase 扩大 |
Firebase Hosting | 网站部署 |
Firebase In-App Messaging | 发送有针对性的上下文音讯 |
Firebase ML | 为应用程序提供机器学习性能 |
Firebase Performance Monitoring | 获取性能剖析 |
Firebase Realtime Database | 能够保留为 JSON 格局的数据库 |
Firebase Remote Config | 容许性能的动态变化 |
Cloud Storage for Firebase | 保留用户创立的内容 |
Test lab | 在虚构设施上验证您的利用 |
Firebase的费用
有两种免费计划
产品 | 价格 | 备注 |
---|---|---|
Spark 计划 | 收费 | 因为是小规模的产品,所以受到限制 |
Blaze 计划 | 随用随付 | 用于大规模的产品 |
无关每个计划的限度和具体价格,请参见官方网站。
<div id="03">3️开发环境</div>
对于开发此计数器应用程序的环境。
对于与以下不同的环境,代码可能会有所不同。
我的项目 | 内容 |
---|---|
PC | Macbook Air(M1) |
Flutter | 3.0.4 |
Firebase CLI | 11.2.2 |
FlutterFire | 0.2.4 |
模拟器 | Android 12(API 31), Chrome |
<div id="04">4️筹备编码</div>
装置Flutter
要装置Flutter,请参考官方网站。
创立计数器应用程序
首先,初始化Flutter应用程序并创立一个计数器应用程序。
flutter create counter_firebase
Firebase CLI的设置
参照官网文档,装置Firebase CLI
这里有几种装置办法,但你也能够实用npm来进行装置
npm install -g firebase-tools
尔后,依照官网文件进行
首先,登录到firebase,全局启用flutterfire_cli
firebase logindart pub global activate flutterfire_cli
从Firebase Console创立一个我的项目
此时应启用Google Analytics
将你的应用程序连贯到Firebase
flutterfire configure
抉择如下
# 抉择我的项目? Select a Firebase project to configure your Flutter application with ›❯ counterfirebase-*** (counterFirebase)# 平台抉择。 查看是否都打了勾? Which platforms should your configuration support (use arrow keys & space to select)? ›✔ android✔ ios✔ macos✔ web# android/build.gradle是否更新? The files android/build.gradle & android/app/build.gradle will be updated to apply Firebase configuration and gradle build plugins. Do you want to continue? (y/n) › yes
在pubspe.yaml
中退出firebase_core
dependencies: firebase_core: ^1.19.2
确保Firebase的配置是最新的
flutterfire configure
在main.dart中装置并初始化Firebase包
import 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart';void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); runApp(const MyApp());}
以下是已实现的应用程序的屏幕截图
小结总结
以下局部已从最后创立的计数器应用程序中更改
- 数目减少是由Riverpod实现的
- 从主页屏幕过渡到计数器屏幕
main.dart
/// Flutter导入import 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';/// Firebase导入import 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart';/// 导入其余页面import 'package:counter_firebase/normal_counter_page.dart';/// 主void main() async { /// Firebase初始化 WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp()));}/// Provider初始化final counterProvider = StateNotifierProvider<Counter, int>((ref) { return Counter();});class Counter extends StateNotifier<int> { Counter() : super(0); /// void increment() => state++;}/// MaterialApp的配置class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter Firebase', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), debugShowCheckedModeBanner: false, ); }}/// 首页屏幕class MyHomePage extends ConsumerWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( title: const Text('My Homepage'), ), body: ListView( padding: const EdgeInsets.all(10), children: const <Widget>[ _PagePushButton( buttonTitle: '计数器', pagename: NormalCounterPage(), ), ], ), ); }}class _PagePushButton extends StatelessWidget { const _PagePushButton({ Key? key, required this.buttonTitle, required this.pagename, }) : super(key: key); final String buttonTitle; final dynamic pagename; @override Widget build(BuildContext context) { return ElevatedButton( child: Container( padding: const EdgeInsets.all(10), child: Text(buttonTitle), ), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => pagename), ); }, ); }}
normal_counter_page.dart
/// Flutterimport 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';/// 其余页面import 'package:counter_firebase/main.dart';class NormalCounterPage extends ConsumerStatefulWidget { const NormalCounterPage({Key? key}) : super(key: key); @override NormalCounterPageState createState() => NormalCounterPageState();}class NormalCounterPageState extends ConsumerState<NormalCounterPage> { @override void initState() { super.initState(); } @override Widget build(BuildContext context) { final counter = ref.watch(counterProvider); return Scaffold( appBar: AppBar( title: const Text('Homepage'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), Text( '$counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { ref.read(counterProvider.notifier).increment(); }, tooltip: 'Increment', child: const Icon(Icons.add), ), ); }}
<div id="05">5️Firebase Analytics编</div>
Firebase Analytics概述
Firebase Analytics是一项服务,容许你应用Firebase将Google Analytics利用到你的应用程序中
剖析容许你记录应用程序的事件,并找出应用程序的应用状况
以下是对于在Flutter中应用剖析的官网文档
筹备
筹备工作和前几章都已实现方可开始
应用办法
要在我的项目中引入firebase_analytics
,将其增加到pubspec.yaml
中并导入
pubspec.yaml
dependencies: firebase_analytics: ^9.2.0
能够记录的事件在firebase_analytics_package
网页上列出
装置
这一次,logEvent记录了屏幕转换事件
import 'package:firebase_analytics/firebase_analytics.dart';class AnalyticsService { Future<void> logPage(String screenName) async { await FirebaseAnalytics.instance.logEvent( name: 'screen_view', parameters: { 'firebase_screen': screenName, }, ); }}
Widget
端的设置如下
ElevatedButton{ child: Text(buttonTitle), onPressed: () { AnalyticsService().logPage(buttonTitle); Navigator.push( context, MaterialPageRoute(builder: (context) => pagename)); },},
日志信息能够在Firebase
控制台找到
在剖析关系中,显示了实时剖析、事件剖析和转换剖析
小结总结
这是与上次相比的变动
- Analytics实现页面转换记录
- 其余代码更改
main.dart
/// Flutter导入import 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';/// Firebase导入import 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart';import 'package:firebase_analytics/firebase_analytics.dart';/// 导入其余页面import 'package:counter_firebase/normal_counter_page.dart';/// 主void main() async { /// Firebase初始化 WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp()));}/// Provider初始化final counterProvider = StateNotifierProvider<Counter, int>((ref) { return Counter();});class Counter extends StateNotifier<int> { Counter() : super(0); void increment() => state++;}/// MaterialApp的配置class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter Firebase', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), debugShowCheckedModeBanner: false, ); }}/// 主屏幕class MyHomePage extends ConsumerWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( title: const Text('My Homepage'), ), body: ListView( padding: const EdgeInsets.all(10), children: const <Widget>[ _PagePushButton( buttonTitle: '计数器', pagename: NormalCounterPage(), ), ], ), ); }}/// 页面过渡按钮class _PagePushButton extends StatelessWidget { const _PagePushButton({ Key? key, required this.buttonTitle, required this.pagename, }) : super(key: key); final String buttonTitle; final dynamic pagename; @override Widget build(BuildContext context) { return ElevatedButton( child: Container( padding: const EdgeInsets.all(10), child: Text(buttonTitle), ), onPressed: () { AnalyticsService().logPage(buttonTitle); Navigator.push( context, MaterialPageRoute(builder: (context) => pagename), ); }, ); }}/// Analyticsclass AnalyticsService { /// 页面转换的日志 Future<void> logPage(String screenName) async { await FirebaseAnalytics.instance.logEvent( name: 'screen_view', parameters: { 'firebase_screen': screenName, }, ); }}
查看Firebase Console
中的内容
<div id="06">6️Firebase Crashlytics编</div>
Firebase Crashlytics概述
Firebase Crashlytics
是一个跟踪利用问题的解体报告工具
Firebase Crashlytics
可用于Android
和iOS
设施
Firebase Crashlytics
的官网文档
筹备
筹备工作和前几章都已实现方可开始
应用办法
要在我的项目中引入firebase_analytics
,将其增加到pubspec.yaml
中并导入
pubspec.yaml
dependencies: firebase_crashlytics: ^2.8.5
为了确保Firebase配置是最新的,在我的项目根目录下关上一个终端,运行flutterfire configure
flutterfire configure
解体处理程序配置
筹备好后,配置解体处理程序
FirebaseCrashlytics.instance.recordFlutterFatalError
会主动抓取Flutter框架内抛出的所有谬误
您还能够应用runZonedGuarded
(须要导入dart:async)来捕获Flutter框架没有捕捉到的谬误
import 'dart:async';import 'package:firebase_crashlytics/firebase_crashlytics.dart';void main() async { /// 解体处理程序 runZonedGuarded<Future<void>>(() async { /// Firebase初始化 WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); /// 解体处理程序(Flutter框架内抛出的所有谬误) FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp())); }, /// 解体处理程序(Flutter框架内未捕捉的谬误) (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));}
测试碰撞
一旦配置好,在安卓或iOS设施上强制解体,进行测试
如果你曾经增加了一个谬误处理程序,调用FirebaseCrashlytics.instance.recordError(error, stack, fatal: true)
,能够在按钮的 onPressed 上应用 throw Exception () 使其解体
TextButton( onPressed: () => throw Exception(), child: const Text("Throw Test Exception"),),
这一次,咱们减少了一个新的解体页面,并创立了一个解体按钮
当解体产生时,Firebase Console的Crashlytics会显示一份报告。
Crashlytics当初将监测应用程序解体的状况
碰撞报告也能够自定义
小结总结
这是与上次相比的变动
- 减少了测试碰撞页面
- 其余代码批改
main.dart
/// Flutter导入import 'package:flutter/material.dart';import 'package:flutter_river:pod/flutter_riverpod.dart';import 'dart:async';/// Firebase导入import 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart';import 'package:firebase_analytics/firebase_analytics.dart';import 'package:firebase_crashlytics/firebase_crashlytics.dart';/// 导入其余页面import 'package:counter_firebase/normal_counter_page.dart';import 'package:counter_firebase/crash_page.dart';void main() async { /// 解体处理程序 runZonedGuarded<Future<void>>(() async { /// Firebase初始化 WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); /// 解体处理程序(Flutter框架内抛出的所有谬误) FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp())); }, /// 解体处理程序(Flutter框架内未捕捉的谬误) (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));}/// Provider初始化final counterProvider = StateNotifierProvider<Counter, int>((ref) { return Counter();});class Counter extends StateNotifier<int> { Counter() : super(0); void increment() => state++;}/// MaterialApp设置class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter Firebase', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), debugShowCheckedModeBanner: false, ); }}/// 主屏幕class MyHomePage extends ConsumerWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( title: const Text('My Homepage'), ), body: ListView( padding: const EdgeInsets.all(10), children: <Widget>[ _PagePushButton( buttonTitle: '计数器', pagename: NormalCounterPage(), ), _PagePushButton( buttonTitle: '解体页面', pagename: CrashPage(), ), ], ), ); }}/// 页面过渡按钮class _PagePushButton extends StatelessWidget { const _PagePushButton({ Key? key, required this.buttonTitle, required this.pagename, }) : super(key: key); final String buttonTitle; final dynamic pagename; @override Widget build(BuildContext context) { return ElevatedButton( child: Container( padding: const EdgeInsets.all(10), child: Text(buttonTitle), ), onPressed: () { AnalyticsService().logPage(buttonTitle); Navigator.push( context, MaterialPageRoute(builder: (context) => pagename), ); }, ); }}/// Analyticsclass AnalyticsService { /// 页面转换的日志 Future<void> logPage(String screenName) async { await FirebaseAnalytics.instance.logEvent( name: 'screen_view', parameters: { 'firebase_screen': screenName, }, ); }}
crash_page.dart
/// Flutterimport 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';class CrashPage extends ConsumerWidget { const CrashPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( title: const Text('解体页面'), ), body: ListView( padding: const EdgeInsets.all(10), children: <Widget>[ TextButton( onPressed: () => throw Exception(), child: const Text("抛出测试异样"), ), ], ), ); }}
<div id="07">7️Firebase Remote Config</div>
Firebase Remote Config概述
Firebase Remote Config
是一项服务,它容许你扭转你的应用程序的行为和外观,而不须要公布更新和近程扭转配置值。
Firebase Remote Config
的官网文档
利用案例
官网介绍了以下 Remote Config 用例。
- 通过百分比推出公布新性能
- 为您的利用定义针对具体平台和针对具体语言区域的促销横幅
筹备
筹备工作和前几章都已实现方可开始
应用办法
要在我的项目中引入firebase_remote_config
,将其增加到pubspec.yaml
中并导入
pubspec.yaml
dependencies: firebase_remote_config: ^2.0.12
创立并执行一个办法来初始化和设置参数
检索单例对象时,管制最小获取距离以获得最佳更新工夫
应用getString()
、getBool()
等办法获取app中应用的参数
import 'package:firebase_remote_config/firebase_remote_config.dart';/// Firebase Remote Config的初始化class FirebaseRemoteConfigService { void initRemoteConfig() async { /// 实例创立 final remoteConfig = FirebaseRemoteConfig.instance; /// 取得一个单例对象 await remoteConfig.setConfigSettings(RemoteConfigSettings( fetchTimeout: const Duration(minutes: 1), minimumFetchInterval: const Duration(minutes: 5), )); /// 在应用程序中设置默认参数值 await remoteConfig.setDefaults(const { "example_param": "Hello, world!", }); /// 取值 await remoteConfig.fetchAndActivate(); }}
加载
初始化
remote_config_page.dart
@overridevoid initState() { super.initState(); /// Firebase Remote Config初始化 FirebaseRemoteConfigService().initRemoteConfig();}
导出到Text Widget时,能够看到输入的是设定值(0)
remote_config_page.dart
Text(FirebaseRemoteConfig.instance.getString("example_param")),
更改值
而后从 Firebase Console的Remote Config设置后端配置以更改值
在参数键中输出由setDefaults确定的键,在默认值中输出新的值,而后'公布更改'
过了一会儿,我可能确认文本已更改为 8
小结总结
这是与上次相比的变动
- 减少了Remote Config页面
- 其余代码批改
main.dart
/// Flutter导入import 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';import 'dart:async';/// Firebase导入import 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart';import 'package:firebase_analytics/firebase_analytics.dart';import 'package:firebase_crashlytics/firebase_crashlytics.dart';import 'package:counter_firebase/remote_config_page.dart';/// 导入其余页面import 'package:counter_firebase/normal_counter_page.dart';import 'package:counter_firebase/crash_page.dart';void main() async { /// 解体处理程序 runZonedGuarded<Future<void>>(() async { /// Firebase初始化 WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); /// 解体处理程序(Flutter框架内抛出的所有谬误) FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp())); }, /// 解体处理程序(Flutter框架内未捕捉的谬误) (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));}/// Provider初始化final counterProvider = StateNotifierProvider<Counter, int>((ref) { return Counter();});class Counter extends StateNotifier<int> { Counter() : super(0); void increment() => state++;}/// MaterialApp设置class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter Firebase', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), debugShowCheckedModeBanner: false, ); }}/// 主屏幕class MyHomePage extends ConsumerWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( title: const Text('My Homepage'), ), body: ListView( padding: const EdgeInsets.all(10), children: <Widget>[ _PagePushButton( buttonTitle: '计数器', pagename: NormalCounterPage(), ), _PagePushButton( buttonTitle: '计数器', pagename: CrashPage(), ), _PagePushButton( buttonTitle: 'Remote Config计数器', pagename: RemoteConfigPage(), ), ], ), ); }}/// 页面过渡按钮class _PagePushButton extends StatelessWidget { const _PagePushButton({ Key? key, required this.buttonTitle, required this.pagename, }) : super(key: key); final String buttonTitle; final dynamic pagename; @override Widget build(BuildContext context) { return ElevatedButton( child: Container( padding: const EdgeInsets.all(10), child: Text(buttonTitle), ), onPressed: () { AnalyticsService().logPage(buttonTitle); Navigator.push( context, MaterialPageRoute(builder: (context) => pagename), ); }, ); }}class AnalyticsService { /// 页面转换的日志 Future<void> logPage(String screenName) async { await FirebaseAnalytics.instance.logEvent( name: 'screen_view', parameters: { 'firebase_screen': screenName, }, ); }}
remote_config_page.dart
/// Flutterimport 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';/// Firebase导入import 'package:firebase_remote_config/firebase_remote_config.dart';/// 其余页面import 'package:counter_firebase/main.dart';class RemoteConfigPage extends ConsumerStatefulWidget { const RemoteConfigPage({Key? key}) : super(key: key); @override RemoteConfigPageState createState() => RemoteConfigPageState();}class RemoteConfigPageState extends ConsumerState<RemoteConfigPage> { @override void initState() { super.initState(); /// Firebase Remote Config初始化 FirebaseRemoteConfigService().initRemoteConfig(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Homepage'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ /// Remote Config数据采集 Text( FirebaseRemoteConfig.instance.getString("example_param"), style: Theme.of(context).textTheme.headline4, ), ], ), ), ); }}/// Firebase Remote Config的初始设置class FirebaseRemoteConfigService { void initRemoteConfig() async { /// 实例创立 final remoteConfig = FirebaseRemoteConfig.instance; /// 取得一个单例对象 await remoteConfig.setConfigSettings(RemoteConfigSettings( fetchTimeout: const Duration(minutes: 1), minimumFetchInterval: const Duration(minutes: 5), )); /// 在应用程序中设置默认参数值 await remoteConfig.setDefaults(const { "example_param": "0", }); /// 获取数值 await remoteConfig.fetchAndActivate(); }}
<div id="08">8️Firebase Authentication</div>
Firebase Authentication概述
Firebase
认证是一项可能应用用户认证性能的服务
官方网站
筹备
筹备工作和前几章都已实现方可开始
应用办法
要在我的项目中引入firebase_auth,请在pubspec.yaml中增加以下内容,并导入它
pubspec.yaml
dependencies: firebase_auth: ^3.4.2
要抉择你的登录形式(电子邮件地址、电话号码等),请从Firebase Console进入认证,在登录形式下抉择你喜爱的登录形式。
在这种状况下,咱们将应用一个电子邮件地址和明码
在Firebase Console中设置好配置后,你就能够施行了
输出TextField中输出的电子邮件地址和明码。
通过将obscureText设置为 "true "使明码不可见。
/// 输出你的电子邮件地址TextField( decoration: const InputDecoration( label: Text('E-mail'), ), controller: _idController,),/// 输出明码TextField( decoration: const InputDecoration( label: Text('Password'), ), controller: _passController, obscureText: true,),
创立一个执行按钮并调用一个容许你创立账户或登录的函数
/// 用于创立账户Container( margin: const EdgeInsets.all(10), child: ElevatedButton( onPressed: () { _createAccount(ref, idController.text, passController.text); }, child: const Text('创立账户'), ),),
应用FirebaseAuth.instance.createUserWithEmailAndPassword
来解决账户创立。
电子邮件地址和明码被传递,如果产生谬误,会产生一个错误信息。
import 'package:firebase_auth/firebase_auth.dart';void _createAccount(String id, String pass) async { try { /// credential 帐户信息记录 final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword( email: id, password: pass, ); } /// 在账户失败的状况下进行错误处理 on FirebaseAuthException catch (e) { /// 如果明码很弱的话 if (e.code == 'weak-password') { print('请设置蕴含大小写字母和数字的6-18位明码'); /// 如果该电子邮件地址曾经在应用中 } else if (e.code == 'email-already-in-use') { print('该电子邮件以注册'); } ///其余谬误 else { print('账户创立谬误'); } } catch (e) { print(e); }}
登录过程是应用FirebaseAuth.instance.signInWithEmailAndPassword
来解决。
它传递电子邮件地址和明码,如果产生谬误,会产生一个错误信息
void _signIn(String id, String pass) async { try { /// credential 帐户信息记录 final credential = await FirebaseAuth.instance.signInWithEmailAndPassword( email: id, password: pass, ); } /// 登录失败时的错误处理 on FirebaseAuthException catch (e) { /// 有效的电子邮件地址 if (e.code == 'invalid-email') { print('有效的电子邮件地址'); } /// 如果该用户不存在 else if (e.code == 'user-not-found') { print('用户不存在'); } /// 如果明码不正确 else if (e.code == 'wrong-password') { print('明码不正确'); } /// 其余谬误 else { print('登录谬误'); } }}
用于登出FirebaseAuth.instance.signOut()
auth_page.dart
void _signOut() async { await FirebaseAuth.instance.signOut();}
获取用户信息的三种形式。
/// 应用authStateChanges、idTokenChanges和userChanges流FirebaseAuth.instance .authStateChanges() .listen((User? user) { if (user != null) { print(user.uid); } });/// 应用由认证(signIn)办法返回的UserCredential对象final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);final user = userCredential.user;print(user?.uid);/// 应用FirebaseAuth实例的currentUser属性if (FirebaseAuth.instance.currentUser != null) { print(FirebaseAuth.instance.currentUser?.uid);}
应用.update*来更新用户材料和电子邮件地址
final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);final user = userCredential.user;await user?.updateDisplayName("Jane Q. User");await user?.updateEmail("janeq@example.com");
通过电子邮件地址进行认证,但也能够通过电话号码和OAuth进行认证
登录前的主屏幕
登录页面
登录后的主屏幕
小结总结
这是与上次相比的变动
- 增加 Firebase 身份验证页面
- 其余代码批改
main.dart
/// Flutter导入import 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';import 'dart:async';/// Firebase导入import 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart';import 'package:firebase_analytics/firebase_analytics.dart';import 'package:firebase_crashlytics/firebase_crashlytics.dart';import 'package:firebase_auth/firebase_auth.dart';/// 导入其余页面import 'package:counter_firebase/normal_counter_page.dart';import 'package:counter_firebase/crash_page.dart';import 'package:counter_firebase/auth_page.dart';import 'package:counter_firebase/remote_config_page.dart';void main() async { /// 解体处理程序 runZonedGuarded<Future<void>>(() async { /// Firebase初始化 WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); /// 解体处理程序(Flutter框架内抛出的所有谬误) FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp())); }, /// 解体处理程序(Flutter框架内未捕捉的谬误) (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));}/// Provider初始化final counterProvider = StateNotifierProvider.autoDispose<Counter, int>((ref) { return Counter();});class Counter extends StateNotifier<int> { Counter() : super(0); void increment() => state++;}/// MaterialApp设置class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter Firebase', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), debugShowCheckedModeBanner: false, ); }}/// 主屏幕class MyHomePage extends ConsumerWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { /// 获取用户信息 FirebaseAuth.instance.authStateChanges().listen((User? user) { if (user == null) { ref.watch(userEmailProvider.state).state = '未登录'; } else { ref.watch(userEmailProvider.state).state = user.email!; } }); return Scaffold( appBar: AppBar( title: const Text('My Homepage'), ), body: ListView( padding: const EdgeInsets.all(10), children: <Widget>[ /// 显示用户信息 Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.person), Text(ref.watch(userEmailProvider)), ], ), /// 页面过渡 const _PagePushButton( buttonTitle: '一般计数器', pagename: NormalCounterPage(), ), const _PagePushButton( buttonTitle: '解体页面', pagename: CrashPage(), ), const _PagePushButton( buttonTitle: '近程配置计数器', pagename: RemoteConfigPage(), ), const _PagePushButton( buttonTitle: '认证页面', pagename: AuthPage(), ), ], ), ); }}/// 页面过渡按钮class _PagePushButton extends StatelessWidget { const _PagePushButton({ Key? key, required this.buttonTitle, required this.pagename, }) : super(key: key); final String buttonTitle; final dynamic pagename; @override Widget build(BuildContext context) { return ElevatedButton( child: Container( padding: const EdgeInsets.all(10), child: Text(buttonTitle), ), onPressed: () { AnalyticsService().logPage(buttonTitle); Navigator.push( context, MaterialPageRoute(builder: (context) => pagename), ); }, ); }}class AnalyticsService { /// 页面转换的日志 Future<void> logPage(String screenName) async { await FirebaseAnalytics.instance.logEvent( name: 'screen_view', parameters: { 'firebase_screen': screenName, }, ); }}
auth_page.dart
/// Flutterimport 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';/// Firebase导入import 'package:firebase_auth/firebase_auth.dart';/// Auth签入状态提供者final signInStateProvider = StateProvider((ref) => '登录或创立一个账户');/// 登录用户的信息提供final userProvider = StateProvider<User?>((ref) => null);final userEmailProvider = StateProvider<String>((ref) => '未登录');/// 页面设置class AuthPage extends ConsumerStatefulWidget { const AuthPage({Key? key}) : super(key: key); @override AuthPageState createState() => AuthPageState();}class AuthPageState extends ConsumerState<AuthPage> { @override void initState() { super.initState(); } @override Widget build(BuildContext context) { final singInStatus = ref.watch(signInStateProvider); final idController = TextEditingController(); final passController = TextEditingController(); return Scaffold( appBar: AppBar( title: const Text('Auth Page'), ), body: ListView( padding: const EdgeInsets.all(10), children: <Widget>[ /// 输出你的电子邮件地址 TextField( decoration: const InputDecoration( label: Text('E-mail'), icon: Icon(Icons.mail), ), controller: idController, ), /// 输出明码 TextField( decoration: const InputDecoration( label: Text('Password'), icon: Icon(Icons.key), ), controller: passController, obscureText: true, ), /// 登录 Container( margin: const EdgeInsets.all(10), child: ElevatedButton( onPressed: () { /// 用于登录 _signIn(ref, idController.text, passController.text); }, style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.grey)), child: const Text('登录'), ), ), /// 创立账户 Container( margin: const EdgeInsets.all(10), child: ElevatedButton( onPressed: () { /// 用于创立账户 _createAccount(ref, idController.text, passController.text); }, child: const Text('创立账户'), ), ), /// 登录信息显示 Container( padding: const EdgeInsets.all(10), child: Text('信息 : $singInStatus'), ), /// 登出 TextButton( onPressed: () { _signOut(ref); }, child: const Text('SIGN OUT')) ], ), ); }}/// 登录解决void _signIn(WidgetRef ref, String id, String pass) async { try { /// 帐户信息被记录在credential final credential = await FirebaseAuth.instance.signInWithEmailAndPassword( email: id, password: pass, ); /// 更新用户信息 ref.watch(userProvider.state).state = credential.user; /// 在屏幕上显示 ref.read(signInStateProvider.state).state = '我曾经可能登录了!'; } /// 登录失败时的错误处理 on FirebaseAuthException catch (e) { /// 有效的电子邮件地址 if (e.code == 'invalid-email') { ref.read(signInStateProvider.state).state = '有效的电子邮件地址'; } /// 该用户不存在 else if (e.code == 'user-not-found') { ref.read(signInStateProvider.state).state = '该用户不存在'; } /// 明码不正确 else if (e.code == 'wrong-password') { ref.read(signInStateProvider.state).state = '明码不正确'; } /// 其余谬误 else { ref.read(signInStateProvider.state).state = '登录谬误'; } }}/// 创立账户void _createAccount(WidgetRef ref, String id, String pass) async { try { /// 帐户信息被记录在credential final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword( email: id, password: pass, ); /// 更新用户信息 ref.watch(userProvider.state).state = credential.user; /// 在屏幕上显示 ref.read(signInStateProvider.state).state = '账户创立胜利!'; } /// 在账户失败的状况下进行错误处理 on FirebaseAuthException catch (e) { /// 如果明码很弱 if (e.code == 'weak-password') { ref.read(signInStateProvider.state).state = '请设置蕴含大小写字母和数字的6-18位明码'); /// 如果该电子邮件地址曾经在应用中 } else if (e.code == 'email-already-in-use') { print('该电子邮件以注册'); } ///其余谬误 else { print('账户创立谬误'); } } catch (e) { print(e); }}/// 登出void _signOut(WidgetRef ref) async { await FirebaseAuth.instance.signOut(); ref.read(signInStateProvider.state).state = '登录或创立一个账户';}
<div id="09">9️Cloud Firestore</div>
Cloud Firestore概述
Cloud Firestore是一个用于无服务器数据存储的NoSQL数据库
官方网站
相似的服务
除了Firestore之外,Firebase也有一个相似的数据库。
云存储,用于存储用户生成的数据,如照片和视频
实时数据库,用于客户与客户之间的实时通信
官方网站上有一个与实时数据库的比拟,以帮忙你抉择哪种数据库
筹备
筹备工作和前几章都已实现方可开始
应用办法
从Firebase Console,抉择Firestore数据库并创立数据库。
Firestore的平安规定已被设置为记录用户ID中的计数,具体如下。
请留神,平安规定在另一章中形容。
rules_version = '2';service cloud.firestore { match /databases/{database}/documents { match /users/{userId}/{documents=**} { allow read, write: if request.auth != null && request.auth.uid == userId } }}
Firestore的数据模型由文档、汇合等组成,反对的数据类型包含bool
、int
和Map
类型,以及日期和地理坐标
在Firebase Console
设置结束后,在pubspec.yaml
中增加以下内容,将cloud_firestore
引入我的项目并导入
pubspec.yaml
dependencies: cloud_firestore: ^3.3.0
Firestore数据处理
显示了向Firestore增加、读取和删除数据的例子。
Riverpod用于写入和读取数据
import 'package:cloud_firestore/cloud_firestore.dart';import 'package:firebase_auth/firebase_auth.dart';/// Firestore数据库的定义final db = FirebaseFirestore.instance;/// 获取UserIDfinal userID = FirebaseAuth.instance.currentUser?.uid ?? 'test';/// 数据增加void add(WidgetRef ref) { final Map<String, dynamic> counterMap = { 'count': ref.read(counterProvider), }; /// 将数据增加到Firestore try { db.collection('users').doc(userID).set(counterMap); } catch (e) { print('Error : $e'); }}/// 数据采集void get(WidgetRef ref) async { try { await db.collection('users').doc(userID).get().then( (event) { ref.read(counterProvider.notifier).state = event.get('count'); }, ); } catch (e) { print('Error : $e'); }}/// 数据删除void delete() async { try { db.collection('users').doc(userID).delete().then((doc) => null); } catch (e) { print('Error : $e'); }}
理论的计数
查看Firebase Console,看看数据是否在Firestore中
小结总结
在这一小结中咱们实现了一下性能
- 减少了Firestore页面
- 扭转页面转换的按钮的色彩
- 其余代码批改
<div id="10">Firebase Realtime Database</div>
Firebase Realtime Database概述
Firebase实时数据库是一个NoSQL数据库服务,它可能在所有的客户端进行数据存储和实时同步
数据以json格局存储,并依据数据量免费,因而它适宜存储比Firestore更频繁更新的小数据
相似的服务
除了Firestore之外,Firebase也有一个相似的数据库。
云存储,用于存储用户生成的数据,如照片和视频
筹备
筹备工作和前几章都已实现方可开始
应用办法
从Firebase Console,抉择实时数据库并创立数据库。
平安规定设置如下,因而,只有内容所有者能够拜访数据库
{ "rules": { "users": { "$uid": { ".read": "auth != null && auth.uid == $uid", ".write": "auth != null && auth.uid == $uid" } } }}
在设置了Firebase Console之后,在pubspec.yaml中增加以下内容,将firebase_database导入我的项目中
dependencies: firebase_database: ^9.0.19
数据操作
数据库定义
检索了用户ID,并定义了数据库
Riverpod用于浏览和写作
import 'package:firebase_database/firebase_database.dart';import 'package:firebase_auth/firebase_auth.dart';final userID = FirebaseAuth.instance.currentUser?.uid ?? '';DatabaseReference dbRef = FirebaseDatabase.instance.ref('users');
写入实时数据库
有两种办法能够向实时数据库写入数据:应用设置和应用更新
这一次是由UPDATE施行的
void write(WidgetRef ref) async { try { await dbRef.update({ '$userID/count': ref.read(counterProvider), }); } catch (e) { print('Error : $e'); }}
读取实时数据库数据
有两种读取实时数据库数据的办法:监听DatabaseReference并调用DatabaseEvent,或应用get()。
前者在每次扭转数据时都会被触发,而后者只读取一次数据。
这次是用get()实现的。
void read(WidgetRef ref) async { try { final snapshot = await dbRef.child(userID).get(); if (snapshot.exists) { ref.read(counterProvider.notifier).state = snapshot.child('count').value as int; } } catch (e) { print('Error : $e'); }}
删除实时数据库数据
能够应用remove()删除数据
void remove() async { try { await dbRef.child(userID).remove(); } catch (e) { print('Error : $e'); }}
Realtime Database的计数器画面
运行后,在Firebase Console查看数据库是否曾经被扭转
小结总结
在这一小结中咱们实现了一下性能
- 减少了实时数据库页面。
- 其余代码批改
<div id="11">▶️Cloud Storage for Firebase</div>
Cloud Firestore概述
Firebase的云存储是一项用于存储用户生成的内容的服务,如照片和视频
相似的服务
除了Firestore之外,Firebase也有一个相似的数据库。
云存储,用于存储用户生成的数据,如照片和视频。
筹备
筹备工作和前几章都已实现方可开始
应用办法
从Firebase控制台中抉择存储来开始。
平安规定曾经设置好了,只有内容所有者能力拜访,具体如下
rules_version = '2';service firebase.storage { match /b/{bucket}/o { match /users/{userId}/{allPaths=**} { allow read, write: if request.auth != null && request.auth.uid == userId; } }}
在设置好Firebase控制台后,增加到pubspec.yaml中,并将firebase_storage导入我的项目中。
这一次,为了在Android上上传图片,image_picker也被一起导入。
留神,Web不反对dart:io包,所以不能应用上面的代码。
dependencies: image_picker: ^0.8.5 firebase_storage: ^10.3.2
数据操作
上传至云存储
要将图片上传到云存储,用image_picker抉择图片并应用putFile
import 'package:image_picker/image_picker.dart';import 'package:firebase_storage/firebase_storage.dart';final userID = FirebaseAuth.instance.currentUser?.uid ?? '';void uploadPic() async { try { final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); File file = File(image!.path); String uploadName = 'image.png'; final storageRef = FirebaseStorage.instance.ref().child('users/$userID/$uploadName'); final task = await storageRef.putFile(file); } catch (e) { print(e); }
治理上传
作为治理上传的一部分,你能够暂停、复原和勾销上传。
此外,你还能够监控上传的进度。
bool paused = await task.pause();print('paused, $paused');bool resumed = await task.resume();print('resumed, $resumed');bool canceled = await task.cancel();print('canceled, $canceled');
能够从Firebase控制台查看图片是否曾经上传
从云存储下载
有两种办法能够从云存储下载图像:下载到内存或间接下载到本地文件夹。
这一次,图像被下载到内存中并显示在应用程序中。
此外,Riverpod还用于浏览和写作。
final imageStateProvider = StateProvider<Uint8List?>((ref) => null);void downloadPic(WidgetRef ref) async { try { String downloadName = 'image.png'; final storageRef = FirebaseStorage.instance.ref().child('users/$userID/$downloadName'); const oneMegabyte = 1024 * 1024; ref.read(imageStateProvider.state).state = await storageRef.getData(oneMegabyte); } catch (e) { print(e); }}
如果显示进去,阐明下载的施行是OK的。
删除云存储数据
应用delete()来删除云存储数据。
void deletePic() async { String deleteName = 'image.png'; final storageRef = FirebaseStorage.instance.ref().child('users/$userID/$deleteName'); await storageRef.delete();}
小结总结
在这一小结中咱们实现了一下性能
- 减少了云存储页面
- 其余代码批改
<div id="12">⬇️Firebase Cloud Messaging</div>
Firebase Cloud Messaging概述
Firebase Cloud Messaging(FCM)是一项容许向客户端应用程序发送推送告诉的服务。
官方网站
FCM的架构也在官网文件中作了介绍
筹备
筹备工作和前几章都已实现方可开始
应用办法
iOS、Android和Web的筹备工作和应用条件有所不同
Android。
Android能够在运行Android4.4或更高版本的设施上运行。
iOS
查看设置阐明,为在iOS上应用做筹备
web
在web上应用时,有必要在Firebase控制台为 "网络推送证书 "生成一对密钥,创立并注册firebase-messaging-sw.js文件,等等
增加到导入到firebase_messaging我的项目中。pubspec.yaml
dependencies: firebase_messaging: ^11.4.0
接待设置
获取令牌的ID。
测试时打印出令牌。
import 'package:firebase_messaging/firebase_messaging.dart';// final fcmToken = await FirebaseMessaging.instance.getToken(vapidKey: 'BDdcxJZSBD...');final fcmToken = await FirebaseMessaging.instance.getToken();print(fcmToken);
如果你也想在后盾接管信息,不论是什么平台,增加以下代码。
_firebaseMessagingBackgroundHandler
函数不能是一个匿名函数,必须被当作一个顶级函数,否则会产生谬误。
Future<void> main() async { FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); runApp(...}Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); print("Handling a background message: ${message.messageId}");}
此外,还为web和iOS设施设置了权限
NotificationSettings settings = await messaging.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true,);
理论调配
测试交付给一个安卓模拟器。
在Firebase控制台的Messaging中抉择一个新的流动。
从 "在设施上测试",复制并粘贴你刚刚打印进去的令牌到FCM注册令牌中。
如果你的设施上收到推送告诉,你就胜利了
小结总结
在这一小结中咱们实现了一下性能
- 其余代码批改
- 例如,有些键是用类来暗藏的
<div id="13">⬅️Firebase In-App Messaging</div>
Firebase In-App Messaging概述
Firebase In-App Messaging是一项容许你发送指标信息的服务
这项服务只在挪动端(iOS、Android)提供
筹备
筹备工作和前几章都已实现方可开始
应用办法
要在我的项目中引入firebase_in_app_messaging
,请将其退出pubspec.yaml
。
因为In-App Messaging
每天只从服务器检索一次信息,咱们将在测试中尝试应用Firebase装置ID(FID)
dependencies: firebase_in_app_messaging: ^0.6.0+14 firebase_app_installations: ^0.1.0+14
在Flutter中能够通过加载firebase_in_app_messaging
来应用利用内音讯
import 'package:firebase_in_app_messaging/firebase_in_app_messaging.dart';
应用FirebaseInstallations来获取FID
import 'package:firebase_app_installations/firebase_app_installations.dart';void getFID() async { String id = await FirebaseInstallations.instance.getId(); print('id : $id');}
交付测试
从Firebase控制台测试交付。
当你筹备好了,输出你刚刚克服的FID,进行设施交付测试
交付后,在调试设施上返回主屏幕一次,并再次关上利用,查看利用内信息
请留神,如果呈现以下错误信息,阐明Firebase In-App Messaging API
被禁用了,你须要拜访谷歌云平台的Firebase In-App Messaging API
并启用该API,如谬误文本所示
PERMISSION_DENIED: Firebase In-App Messaging API has not been used in project *** before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/firebaseinappmessaging.googleapis.com/overview?project=*** then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
小结总结
在这一小结中咱们实现了一下性能
- In-App Messaging的施行
<div id="14">◀️Firebase ML</div>
Firebase ML概述
Firebase ML是一个应用Firebase的机器学习模型推理
Firebase x Flutter中提供的机器学习推理
应用Flutter和Firebase的机器学习推理能够在设施上或在云端实现
设施上的推理
应用Firebase进行设施上的推理意味着用Firebase ML提供自定义的TensorFlowLite模型进行本地推理
对于理论的推断,应用了tflite_flutter、ML Kit等
用Firebase提供定制的TF模型的益处是,用户能够应用最新的模型,而不须要更新他们的应用程序
云端推理
应用Firebase(谷歌云)进行云推理,意味着用云视觉AI或云自然语言进行推理。
目前,没有为Flutter提供API,所以你须要为每个操作系统组合API
除上述之外,还有其余办法能够应用来自其余服务的机器学习模型
如何用Firebase部署自定义的TensorflowLite模型散布
筹备一个你本人训练的自定义TF模型的TFLite文件
在这种状况下,为了执行图像识别工作,我从TensorFlow Hub取得了Imagenet图像分类训练模型
请留神,当从TensorFlow Hub下载模型时,要留神许可证和下载的文件类型
一旦文件筹备好了,从Firebase控制台的机器学习中部署该模型
Firebase官网文档举荐应用tflite_flutter和tflite进行推理,但我在开发环境中无奈用导入的tflite_flutter等构建利用,所以我用ML Kit做了试验
能够应用ML工具包中的TFLite自定义模型的工作包含图像标签或物体检测和跟踪
因为咱们将对图像标签进行推理,咱们也将介绍google_ml_kit
和google_mlkit_image_labelling
dependencies: google_ml_kit: ^0.11.0 google_mlkit_image_labeling: ^0.3.0
应用google_mlkit_image_labelling
包中的FirebaseImageLabelerModelManager
从Firebase ML下载模型
final bool response = await FirebaseImageLabelerModelManager().downloadModel(modelname);final options = FirebaseLabelerOption( confidenceThreshold: 0.5, modelName: modelname, maxCount: 3);_imageLabeler = ImageLabeler(options: options);
如果你晓得照片的门路,例如image_picker,你只须要两行代码进行根本的标签推理
final InputImage inputImage = InputImage.fromFilePath(path);final List labels = await _imageLabeler.processImage(inputImage);
从推断出的后果中提取标签
String labelText = '';for (final label in labels) { labelText += '\nLabel: ${label.label}';}
用应用程序查看。
它仿佛可能进行推论,但后果与我预期的不同,所以还有改良的余地
小结总结
在这一小结中咱们实现了一下性能
- 增加ml_page页面
- 其余代码批改
<div id="15">⏬Cloud Functions for Firebase</div>
Cloud Functions for Firebase概述
Cloud Functions for Firebase是一项服务,它能够对触发的事件主动执行后端代码
Firebase的云性能反对用javascript和typescript编写,容许在不治理或扩大服务器的状况下实现后端
利用案例
筹备
筹备工作和前几章都已实现方可开始
在JavaScript环境中运行Cloud Functions须要一个Node.js环境,如果你还没有这样做,请应用nvm装置它
应用办法
装置firebase-tools
npm install -g firebase-tools
初始化我的项目
在Firebase登录筹备好的环境中,初始化函数和其余必要的工具。
在这种状况下,这次我抉择了语言JavaScript
firebase init functions
一旦初始化,我的项目中就会创立一个新的函数文件夹
创立性能
在function/index.js中编写执行Cloud Functions的函数。
导入必要的模块
const functions = require("firebase-functions");
性能定义。
有三种次要的办法来调用一个函数
- 如何间接从应用程序调用
- 如何通过 HTTP 申请调用函数
- 如何调入日程设置
这一次,计数性能是间接从应用程序中调用的,所以在后端应用了onCall触发器。
当咱们在做这件事的时候,咱们还将试验可能调用UID
exports.functionsTest = functions.https.onCall((data, context) => { const firstNumber = data.firstNumber; const secondNumber = data.secondNumber; const addNumber = firstNumber + secondNumber; const contextUid = context.auth.uid; return { addNumber:addNumber, contextUid:contextUid }});
在模拟器上测试
在部署之前在本地模拟器上进行测试以查看有限循环等。
如果App Check应用,则无奈运行模拟器,须要应用 App Check 调试提供程序,每个执行环境都必须应用调试提供程序
Java装置 Open JDK,因为它须要启动模拟器
装置并初始化本地模拟器,必要时用Firebase init *装置每个插件
firebase init emulators
当应用本地模拟器时,在Flutter侧的主函数中设置useFunctionsEmulator
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // Ideal time to initialize FirebaseFunctions.instance.useFunctionsEmulator('localhost', 5001);...
装置后,用emulators:start启动模拟器,并在浏览器中关上http://localhost:4000/
(默认状况下)
firebase emulators:start
部署
如果一切顺利,就部署到生产环境
在Firebase控制台,进入性能,抉择 "开始"
firebase deploy --only functions:functionsTest
从Flutter应用程序调用
要在你的Flutter我的项目中利用云函数,请在pubspec.yaml中引入cloud_functions
dependencies: cloud_functions: ^3.3.2
functions编写执行的代码
import 'package:cloud_functions/cloud_functions.dart';void addNumber() async { try { final result = await FirebaseFunctions.instance .httpsCallable('functionsTest') .call({'firstNumber': _number, 'secondNumber': 1}); _number = result.data['addNumber']; print(result.data['contextUid']); } on FirebaseFunctionsException catch (error) { print(error.code); print(error.details); print(error.message); }}****
一款应用 Cloud Functions 进行计数的奢华应用程序曾经实现
小结总结
在这一小结中咱们实现了一下性能
- 增加了Cloud Functions
- 增加了cloud_functions
- 其余代码批改
const functions = require("firebase-functions");exports.functionsTest = functions.https.onCall(async(data, context) => { const firstNumber = data.firstNumber; const secondNumber = data.secondNumber; const addNumber = firstNumber + secondNumber; const contextUid = context.auth.uid; return { addNumber:addNumber, contextUid:contextUid }});
import 'dart:async';import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';import 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart';import 'package:firebase_analytics/firebase_analytics.dart';import 'package:firebase_crashlytics/firebase_crashlytics.dart';import 'package:counter_firebase/remote_config_page.dart';import 'package:firebase_auth/firebase_auth.dart';import 'package:firebase_messaging/firebase_messaging.dart';import 'package:firebase_app_installations/firebase_app_installations.dart';import 'package:firebase_in_app_messaging/firebase_in_app_messaging.dart';import 'package:counter_firebase/normal_counter_page.dart';import 'package:counter_firebase/crash_page.dart';import 'package:counter_firebase/auth_page.dart';import 'package:counter_firebase/firestore_page.dart';import 'package:counter_firebase/realtime_database_page.dart';import 'package:counter_firebase/cloud_storage.dart';import 'package:counter_firebase/cloud_functions_page.dart';import 'package:counter_firebase/ml_page.dart';final isAndroid = defaultTargetPlatform == TargetPlatform.android ? true : false;final isIOS = defaultTargetPlatform == TargetPlatform.iOS ? true : false;Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); print('Handling a background message: ${message.messageId}');}void main() async { runZonedGuarded<Future<void>>(() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; // FirebaseFunctions.instance.useFunctionsEmulator('localhost', 5001); /// runApp w/ Riverpod runApp(const ProviderScope(child: MyApp())); }, (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));}final counterProvider = StateNotifierProvider<Counter, int>((ref) { return Counter();});class Counter extends StateNotifier<int> { Counter() : super(0); void increment() => state++;}class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter Firebase', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), debugShowCheckedModeBanner: false, ); }}class MyHomePage extends ConsumerStatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override MyHomePageState createState() => MyHomePageState();}class MyHomePageState extends ConsumerState<MyHomePage> { @override void initState() { super.initState(); FirebaseMessagingService().setting(); FirebaseMessagingService().fcmGetToken(); FirebaseInAppMessagingService().getFID(); } @override Widget build(BuildContext context) { FirebaseAuth.instance.authStateChanges().listen((User? user) { if (user == null) { ref.watch(userEmailProvider.state).state = '未登录'; } else { ref.watch(userEmailProvider.state).state = user.email!; } }); return Scaffold( appBar: AppBar( title: const Text('My Homepage'), ), body: ListView( padding: const EdgeInsets.all(10), children: <Widget>[ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.person), Text(ref.watch(userEmailProvider)), ], ), const _PagePushButton( buttonTitle: '计数器', pagename: NormalCounterPage(), ), const _PagePushButton( buttonTitle: '解体页面', pagename: CrashPage(), ), const _PagePushButton( buttonTitle: 'Remote Config计数器', pagename: RemoteConfigPage(), ), const _PagePushButton( buttonTitle: '机器学习页面', pagename: MLPage(), ), const _PagePushButton( buttonTitle: '验证页面', pagename: AuthPage(), bgColor: Colors.red, ), /// 过渡到每个页面(认证后可用) /// 让未经受权的人无奈按下按钮 FirebaseAuth.instance.currentUser?.uid != null ? const _PagePushButton( buttonTitle: 'Firestore计数器', pagename: FirestorePage(), bgColor: Colors.green, ) : const Text('Firestore验证后,即可关上柜台'), FirebaseAuth.instance.currentUser?.uid != null ? const _PagePushButton( buttonTitle: 'Realtime Database计数器', pagename: RealtimeDatabasePage(), bgColor: Colors.green, ) : const Text('Realtime Database认证,以关上计数器'), FirebaseAuth.instance.currentUser?.uid != null ? const _PagePushButton( buttonTitle: 'Cloud Storage页', pagename: CloudStoragePage(), bgColor: Colors.green, ) : const Text('Cloud Storage请认证以关上该页面'), FirebaseAuth.instance.currentUser?.uid != null ? const _PagePushButton( buttonTitle: 'Cloud Functions页', pagename: CloudFunctionsPage(), bgColor: Colors.green, ) : const Text('Cloud Functions请认证以关上该页面'), ], ), ); }}class _PagePushButton extends StatelessWidget { const _PagePushButton({ Key? key, required this.buttonTitle, required this.pagename, this.bgColor = Colors.blue, }) : super(key: key); final String buttonTitle; final dynamic pagename; final Color bgColor; @override Widget build(BuildContext context) { return ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(bgColor), ), child: Container( padding: const EdgeInsets.all(10), child: Text(buttonTitle), ), onPressed: () { AnalyticsService().logPage(buttonTitle); Navigator.push( context, MaterialPageRoute(builder: (context) => pagename), ); }, ); }}class AnalyticsService { Future<void> logPage(String screenName) async { await FirebaseAnalytics.instance.logEvent( name: 'screen_view', parameters: { 'firebase_screen': screenName, }, ); }}class FirebaseMessagingService { FirebaseMessaging messaging = FirebaseMessaging.instance; void setting() async { NotificationSettings settings = await messaging.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true, ); print('User granted permission: ${settings.authorizationStatus}'); } void fcmGetToken() async { if (isAndroid || isIOS) { final fcmToken = await messaging.getToken(); print(fcmToken); } else { final fcmToken = await messaging.getToken( vapidKey: FirebaseOptionMessaging().webPushKeyPair); print('web : $fcmToken'); } }}class FirebaseInAppMessagingService { void getFID() async { String id = await FirebaseInstallations.instance.getId(); print('id : $id'); }}
/// Flutterimport 'package:flutter/material.dart';/// Firebaseimport 'package:cloud_functions/cloud_functions.dart';class CloudFunctionsPage extends StatefulWidget { const CloudFunctionsPage({Key? key}) : super(key: key); @override CloudFunctionsPageState createState() => CloudFunctionsPageState();}class CloudFunctionsPageState extends State<CloudFunctionsPage> { int _number = 0; void addNumber() async { try { final result = await FirebaseFunctions.instance .httpsCallable('functionsTest') .call({'firstNumber': _number, 'secondNumber': 1}); _number = result.data['addNumber']; } on FirebaseFunctionsException catch (error) { print(error.code); print(error.details); print(error.message); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Cloud Functions页'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), Text( '$_number', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { addNumber(); }); }, tooltip: 'Increment', child: const Icon(Icons.add), ), ); }}
<div id="16">⏫Firebase Hosting</div>
Firebase Hosting概述
Firebase Hosting是一个为网络应用、动态和动静内容以及微服务提供的托管服务
如果您想将您的Flutter利用作为一个网络应用托管,能够应用
利用案例
筹备
筹备工作和前几章都已实现方可开始
应用办法
有两种办法来部署到Firebase Hosting
- 通过输出命令进行部署
- 应用Github Actions进行部署
在这种状况下,咱们抉择通过输出命令进行部署
创立一个Firebase我的项目,装置Firebase CLI并初始化我的项目
firebase init hosting
问题和答案示例如下
? What do you want to use as your public directory? (public)build/web? Configure as a single-page app (rewrite all urls to /index.html)? (y/N)No? Set up automatic builds and deploys with GitHub? (y/N)No
为网络配置设置
在web/index.html
中,配置html <head>
设置
在web/manifest.json
中,配置网络应用的行为和图标
能够通过笼罩web/icons
目录下的文件(如Icon-192.png)来扭转应用程序的图标。
在这一点上要留神不要弄错尺寸。
调试完应用程序后,在Flutter端构建应用程序,而后用Firebase部署它
flutter build webfirebase deploy --only hosting
拜访部署后呈现的URL,如果应用程序被验证,就能够应用了
留神,如果你想应用一个自定义的域名,你能够从Firebase Console进行设置
<div id="17">↩️Firebase Performance Monitoring</div>
Firebase Performance Monitoring概述
Firebase Performance Monitoring是一项容许您掂量您的Flutter利用性能的服务
点击这里查看Performance Monitoring官网文件
应用办法
要在我的项目中引入firebase_performance,请在pubspec.yaml中增加以下内容
dependencies: firebase_performance: ^0.8.2
为了确保Firebase的配置是最新的,在我的项目根目录下关上一个终端,运行flutterfire configure
为了显示最后的性能数据,运行该我的项目并查看它是否显示在Firebase控制台。
如果显示了图表,你就胜利了。
自定义性能测量容许你增加自定义属性以及通用属性。
<div id="18">Firebase 其余服务</div>
本章概述
本章总结了前几章中没有介绍的Firebase服务
Firebase Dynamic Links(仅限挪动端)
Firebase Dynamic Links
是一项提供 "动静链接 "的服务,能够让你间接进入挪动原生利用中的链接内容
要在Flutter中构建,请将Firebase_dynamic_links
导入你的我的项目,并从Firebase控制台创立链接
Firebase App Check
App Check是一项爱护后端资源不被滥用的服务,如计费欺诈和网络钓鱼
应用reCAPTCHA或其余形式查看设施是否被信赖。
所应用的认证供应商将因平台而异
平台 | 提供者 |
---|---|
Apple platforms | DeviceCheck, App Attest |
Android | Play Integrity, SafetyNet |
web | reCAPTCHA v3, reCAPTCHA Enterprise |
它也能够与 Flutter 一起应用
如果flutterfire装置在你的环境中,从Firebase Console启用并初步配置App Check,并装置App Check库
flutter pub add firebase_app_check
调用执行 App Check 的代码,运行它,你就功败垂成了
import 'package:flutter/material.dart';import 'package:firebase_core/firebase_core.dart';// Import the firebase_app_check pluginimport 'package:firebase_app_check/firebase_app_check.dart';Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); await FirebaseAppCheck.instance.activate( webRecaptchaSiteKey: 'recaptcha-v3-site-key', ); runApp(App());}
Firebase installations service
Firebase installations service负责管理Firebase的装置
用来查看Firebase的装置ID,用于Messaging、Analytics等
Firebase Google AdMob
Firebase Google Admob是一项挪动广告服务
如果你想在Flutter中启动AdMob广告,你不能应用Firebase的Admob,必须应用挪动广告SDK(Flutter)(beta)
Test Lab
Test Lab是一项容许你在云端托管的设施上测试你的应用程序的服务,当你想在各种配置上测试你的应用程序时能够应用。
你能够通过将你的应用程序部署到Test Lab,并从Firebase Console将文件上传到Robo Test,来测试你的挪动应用程序。
官网文档
Firebase App Distribution
Firebase App Distribution是一项促成向测试人员散发应用程序的服务
与Google Play和App Store的链接使散发应用程序变得容易
你能够从Firebase Console散发应用程序并治理测试人员
官网文档
Firebase Extensions
Firebase Extensions是一项服务,它容许你应用打包的解决方案疾速为你的应用程序增加性能。
在官网的Firebase Extensions中能够找到许多扩大,这些扩大是应用Cloud Functions for Firebase编写的
[
](https://firebase.google.com/p...)
请留神,Firebase Extensions的装置只实用于Blaze打算(按需付费)
<div id="19">Firebase 平安规定</div>
什么是Firebase的平安规定?
Firebase平安规定是定义如何容许拜访存储数据的语法
Firestore、Realtime Database、Cloud Storage下列各项的语法是不同的
Firestore的语法
Firestore平安规定的语法是基于Common Expression Language(CEL)
语言
作为根本的平安规定,在所有通过身份验证的用户都能够拜访的测试环境中,编写
service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth != null; } }}
在只有内容所有者有权拜访的生产环境中,编写
service cloud.firestore { match /databases/{database}/documents { // Allow only authenticated content owners access match /some_collection/{userId}/{documents=**} { allow read, write: if request.auth != null && request.auth.uid == userId } }}
Realtime Database
Realtime Database应用json格局的平安规定的语法。
为确保只有内容所有者能力拜访,请按以下形式进行配置。
{ "rules": { "users": { "$uid": { ".read": "auth != null && auth.uid == $uid", ".write": "auth != null && auth.uid == $uid" } } }}
Cloud Storage
云存储平安规定的语法是基于Common Expression Language(CEL)
只能由内容所有者拜访的平安规定包含
rules_version = '2';service firebase.storage { match /b/{bucket}/o { match /users/{userId}/{allPaths=**} { allow read, write: if request.auth != null && request.auth.uid == userId; } }}
<div id="20"> 参考网站</div>
在编写本文时参考了一些网站
在此表示感谢
本文由mdnice多平台公布