我们将使用Google登录来验证应用程序的用户。Google登录功能可让用户使用其Google帐户(与Gmail、Play、照片和其他Google服务所使用的帐户相同的帐户)进行安全登录。我们还可以根据与用户的Google帐户相关联的个人资料和身份信息,个性化用户体验。用户登录后,我们可以使用个人资料照片个性化聊天消息头像。
要添加对Google登录的支持,我们将使用google_sign_in
插件,在main.dart
文件中导入相应的包。
import 'package:google_sign_in/google_sign_in.dart';
要启用Google登录,需要在浏览器中打开Firebase控制台并选择我们的项目。导航到Authentication > 登陆方法
。启用Google提供商,具体如下:
要在iOS上配置Google登录,需要确保生成的GoogleService-Info.plist
文件位于Xcode中Runner项目的Runner目录中,因此Google登录框架可以确定您的客户端ID。将客户端ID的捆绑ID和反向URL添加到应用的Info.plist
文件的主要字典中:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- 反转客户端ID的URL -->
<string>com.googleusercontent.apps.462578386393-kisbgopib3t6plf4dgv3s0n4ur3svjmo</string>
<!-- 捆绑ID -->
<string>com.yourcompany.friendlychat</string>
</array>
</dict>
</array>
我们可以在GoogleService-Info.plist
文件中找到客户端ID的反向URL,复制REVERSED_CLIENT_ID
字符串的值并粘贴以将其添加到CFBundleURLSchemes
。当用户登录到应用程序时,此URL类型将处理回调。
第一次启动应用程序时,可能需要一两分钟才能启动。在iOS上,需要额外的时间来初始化Cocapods repo,而在Android上,需要下载maven的依赖关系。在下一组更改之后,我们将能够重新加载应用程序,并且开发周期将快得多。
现在我们的应用程序仅限于单个用户和设备,当用户发送消息时,应用程序将其标记为_name
变量的值,并将其显示在同一个屏幕上,头像是一个简单的彩色圆圈。我们现在需要做的是,多个用户将能够通过实时数据库共享消息。为了有利于扩展应用程序,我们将个性化头像以区分用户。由于我们将拥有发件人的Google登录凭据,因此我们可以重用用户的个人资料照片。我们将在稍后的步骤中添加数据库支持。
首先,添加一个名为googleSignIn
的全局变量,使用新的GoogleSignIn
实例初始化它,我们将用它来调用Google登录API。在main.dart
文件添加以下代码。
final googleSignIn = new GoogleSignIn();
现在定义两个私有方法,一个用于登录,另一个用于发送消息。添加_ensureLoggedIn()
方法来检查GoogleSignIn
实例的currentUser
属性。在ChatScreenState
中添加_ensureLoggedIn()
方法定义。
class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
//...
Future<Null> _ensureLoggedIn() async {
GoogleSignInAccount user = googleSignIn.currentUser;
if (user == null)
user = await googleSignIn.signInSilently();
if (user == null) {
await googleSignIn.signIn();
}
}
//...
}
以前的代码片段使用多个等待表达式依次执行Google登录方法。如果currentUser
属性的值为null
,应用程序将首先执行signInSilently()
,获取结果并将其存储在user
变量中。signInSilently
方法尝试在没有交互的情况下登录之前经过身份验证的用户。此方法执行完毕后,如果user
值仍为null
,则应用程序将通过执行signIn()
方法启动登录过程。用户登录后,我们可以从GoogleSignIn
实例访问配置文件照片。
现在提交消息的过程有两步,认证和发送。我们需要协调工作,以便首先认证,如果成功,那么用户可以发送消息。首先,将_handleSubmitted
方法从ChatScreenState
拆分为两种单独的方法。而不是在_handleSubmitted()
中执行所有提交的工作,我们将使用它来协调其他方法执行的任务,例如验证用户是否登录并发送消息。
在_handleSubmitted()
中保留_textController.clear()
和setState()... _isComposing
方法调用。添加_ensureLoggedIn()
和_sendMessage()
调用,如下面代码所示。
class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
//...
Future _handleSubmitted(String text) async {
_textController.clear();
setState((){
_isComposing = false;
});
await _ensureLoggedIn();
_sendMessage(text: text);
}
//...
}
修改_handleSubmitted()
的签名,表示它是一个不返回任何内容的异步方法。在尝试发送消息之前,将调用添加到await _ensureLoggedIn()
以等待用户验证成功。现在我们来定义_sendMessage()
,将_handleSubmitted()
的其余部分添加到新的私有方法中。我们将使text
成为一个命名参数,以便稍后可以向_sendMessage
添加更多的String
参数。
class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
//...
void _sendMessage({ String text }) {
ChatMessage message = new ChatMessage(
text: text,
animationController: new AnimationController(
duration: new Duration(milliseconds: 300),
vsync: this
)
);
setState((){
_messages.insert(0, message);
});
message.animationController.forward();
}
//...
}
为了个性化头像,我们在ChatMessage
类的build()
方法中使用新的GoogleUserCircleAvatar
对象替换CircleAvatar
控件,以及其Text
控件。CircleAvatar
可以使用网络图像,但是GoogleUserCircleAvatar
帮助器类可以轻松地从GoogleSignIn
实例获取正确大小的配置文件照片。google_sign_in.dart
插件定义了这个类。
class ChatMessage extends StatelessWidget {
//...
@override
Widget build(BuildContext context) {
return new SizeTransition(
//...
child: new Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
margin: const EdgeInsets.only(right: 16.0),
child: new GoogleUserCircleAvatar(googleSignIn.currentUser.photoUrl),
),
//...
]
)
)
);
}
}
currentUser
属性是身份验证对象,我们使用photoUrl
获取新头像的图像。现在,我们需要个性化显示用户名。以前,我们使用硬编码显示用户名。现在我们已经集成了Google登录,我们可以删除_name
全局变量。改成从已登录的Google用户处获取用户名。在ChatMessage
类中使用来自GoogleSignIn
实例的displayName
设置_name
变量。
class ChatMessage extends StatelessWidget {
//...
@override
Widget build(BuildContext context) {
return new SizeTransition(
//...
child: new Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//...
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
googleSignIn.currentUser.displayName,
style: Theme.of(context).textTheme.subhead),
new Container(
margin: const EdgeInsets.only(top: 5.0),
child: new Text(text),
)
]
)
]
)
)
);
}
}
现在,当我们发送消息时,头像和发件人姓名与您的Google帐户中的个人资料信息相匹配。随着我们继续进行更改并优化应用程序的UI,我们可以快速查看结果,而不需要重新启动完整的应用程序。使用Flutter的热重新加载功能将更新的源文件注入正在运行的Dart虚拟机(Dart Virtual Machine)并刷新UI。热重载是实验、原型设计和迭代的强大工具。