应用 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 login
dart 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
/// Flutter
import '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),
);
},
);
}
}
/// Analytics
class 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),
);
},
);
}
}
/// Analytics
class AnalyticsService {
/// 页面转换的日志
Future<void> logPage(String screenName) async {
await FirebaseAnalytics.instance.logEvent(
name: 'screen_view',
parameters: {
'firebase_screen': screenName,
},
);
}
}
crash_page.dart
/// Flutter
import '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
@override
void 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
/// Flutter
import '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
/// Flutter
import '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;
/// 获取UserID
final 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');
}
}
/// Flutter
import 'package:flutter/material.dart';
/// Firebase
import '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 web
firebase 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 plugin
import '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多平台公布
发表回复