赞
踩
Flutter是一个由谷歌开发的开源应用程序框架。
Flutter/Dart
有一套完整的官方文档。 这里有一些例子
关于如何开发Flutter应用程序的文档。 Flutter官方文档
此外,Flutter一年比一年受欢迎,除了官方文档外,许多开发者在其他网站上整理了一些通俗易懂的文章,可以作为开发的参考。
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 | 在虚拟设备上验证您的应用 |
有两种收费方案
产品 | 价格 | 备注 |
---|---|---|
Spark 方案 | 免费 | 由于是小规模的产品,所以受到限制 |
Blaze 方案 | 随用随付 | 用于大规模的产品 |
有关每个方案的限制和详细价格,请参见官方网站。
关于开发此计数器应用程序的环境。
对于与以下不同的环境,代码可能会有所不同。
项目 | 内容 |
---|---|
PC | Macbook Air(M1) |
Flutter | 3.0.4 |
Firebase CLI | 11.2.2 |
FlutterFire | 0.2.4 |
模拟器 | Android 12(API 31), Chrome |
要安装Flutter,请参考官方网站。
首先,初始化Flutter应用程序并创建一个计数器应用程序。
flutter create counter_firebase
参照官方文档,安装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());
}
以下是已完成的应用程序的屏幕截图
以下部分已从最初创建的计数器应用程序中更改
「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),
),
);
}
}
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
控制台找到
在分析关系中,显示了实时分析、事件分析和转换分析
这是与上次相比的变化
「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
中的内容
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("抛出测试异常"),
),
],
),
);
}
}
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
这是与上次相比的变化
「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();
}
}
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进行认证
登录前的主屏幕
登录页面
登录后的主屏幕
这是与上次相比的变化
「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 = '登录或创建一个账户';
}
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添加、读取和删除数据的例子。 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中
在这一小结中我们完成了一下功能
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。