使用服务类来设计Flutter中的认证API
服务类是一种很好的方法,当从你的代码中的许多地方调用一个API方法时,可以将第三方代码的实现细节排除在你的软件之外。服务类的一个副作用是,它通过你的服务的API产生一个特定于你的应用程序的领域语言,所以你的视图模型可以被读取。
服务类将有助于学习如何封装和隔离第三方库和API。我们将在本教程中使用Firebase认证。
前提条件
在我们开始之前,读者需要具备以下条件。
- 在他们的机器上安装了Flutter。
- 对Flutter有一些基本的认识和了解。
何时创建服务类
当你在你的应用程序中为它构建API时,你可以创建一个服务类。它将为应用程序的其他部分提供API功能,包括。
- 从代码库中删除第三方包。
- 在不同的视图模型之间交换功能。
你可能会开发一个服务的典型场景
- 当本地存储可以被读写的时候。
- 当利用网络API的时候。
- 当创建一个用户帐户时。
- 当进行一些复杂的计算时。
- 当用包装器包装firebase或其他第三方包时。
认证状态
认证状态可以通过一个流来订阅,使用FirebaseAuth 。这个流在每次用户的认证状态发生变化时都会产生一个事件,并且会无限期地持续下去。
对于使用服务类作为API封装的终端用户或应用程序来说,没有任何实现细节需要透露。Firebase认证是通过下面的代码设置的。检索一个Firebase认证对象就像调用ProviderFirebaseAuth>(context) 函数一样简单。
import 'package:firebase_auth/firebase_auth.dart';
FirebaseAuth auth = FirebaseAuth.instance;
class Login extends StatelessWidget {
//sign in activity
Future<void> _signInAnonymously() async {
try {
FirebaseAuth=> Firebase Authentication
final firebaseAuth = Provider.of<FirebaseAuth>(context);
await firebaseAuth.signInAnonymously();
} catch(e) {
print(e);
}
}
}
注意:虽然许多程序不需要用户明确地登录,但单独识别他们是很关键的。
交换偏好、授予访问权、编译数据和要求本地认证的能力是一些最常见的安全功能。可以通过以下方式提高安全性。
- 验证一个请求是否来自一个已经登录到你的应用程序的用户。
- 验证该用户是否使用Firebase数据库、实时数据库或外部API。
尽管我们在代码中使用了firebase认证的API,但我们仍然在直接访问它,而这可能会导致几个问题。
- 首先,你将如何处理Firebase认证的变化,使事情在未来变得更糟。
- 假设我们决定在将来使用另一个认证服务而不是firebase。我们将不得不对我们的计划做一些改变。
我们的程序对firebase的认证调用将被升级以解决上面的问题。此外,随着我们项目范围的扩大,我们可以预期会在我们的应用程序中添加更多的内容。
创建服务类
你可以使用firebase的API构建一个通用的认证系统。服务类只不过是底层类的克隆。在我们下面的示例代码中,使用Firebase Authentication Service 类演示了firebase认证API的调用。
class TheUser {
const TheUser({@required this.id});
final String id;
}
class FirebaseAuthService {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
// It is possible to create a new user by calling the 'FirebaseTheUser' private method "TheUser".
TheUser _userFromFirebase(User theuser)
{
return theuser == null? null : TheUser(id: theuser.id);
}
Stream<TheUser> get onAuthStateChanged {
return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
}
Future<TheUser> signInAnonymously() async
{
return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
return_userFromFirebase(theuser);
}
Future<void> logOut() async
{
return_firebaseAuth.logOut();
}
}
下面我们更新我们的顶层小部件,以利用新的服务类。
class ThisApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return Provider<FirebaseAuthService>(
builder:(_)=>FirebaseAuthService(),
child: MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: LandingPage(),
),
);
}
}
我们的Firebase认证服务类将是唯一一个被Firebase认证的突破性变化所影响的。没有必要创建一个基类,但如果你选择这样做,下面是一个代码的例子。
abstract class AuthService {
//creation of a base class
Future<TheUser>loginAnonymously();
Future<void>logOut();
Stream<TheUserget onAStateChanged;
}
class FirebaseAuthService implements AuthService {
}
使用基类的类型,建立一个子类的实例。
class ThisApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider<AuthService>(
// base class
builder:(_)=>FirebaseAuthService(),
// our concrete subclass
child: MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: LandingPage(),
),
);
}
}
注意。
- 引入基类是一种退步,一次写一个服务类也是一种选择。
- 在现代集成开发环境中,类的重命名和它们的使用变得简单。例如,当涉及到认证服务时,Firebase和一个假的认证服务可以在运行时互换,用于测试和演示。
创建一个持有应用程序状态的服务
第一步:创建一个服务类
NewAbstract.dart 是你应该创建的文件的名称。创建一个抽象类,在其中定义你的服务的能力。
abstract class newServiceClass {
Future<int> getSomeValue();
Future<void> doSomething(int value);
}
第二步:服务的实现
创建一个名为newImplService.dart 的新文件。扩展你的newServiceClass 类并添加以下方法。
import 'NewAbstract.dart';
class newImplService extends newServiceClass {
@override
Future<int> getSomeValue() async {
// do something
return someValue;
}
@override
Future<void> doSomething(int value) async {
// do something
}
}
第三步:服务定位器和初始化
创建一个名为locatingService.dart 的文件并将GetIt服务定位器包添加到pubspec.yaml 。注册你的服务的实现。
import 'package:get_it/get_it.dart';
import 'newImplService.dart';
import 'NewAbstract.dart';
GetIt ourGetIt = GetIt.instance;
setupServiceLocator() {
ourGetIt.registerLazySingleton<newServiceClass>(() => newImplService());
}
在main.dart 中运行应用程序之前,初始化服务定位。
void main() {
setupServiceLocator();
runApp(MyApp());
}
第四步:使用该服务
在这一步,我们将使用服务定位器从代码的任何地方获得对我们服务的引用。
class MyClass {
newServiceClass _newServiceClass = locator<newServiceClass>();
}
这就是你在该类中的实现方式。
_newServiceClass.getSomeValue()_newServiceClass.doSomething(someValue)
这里有一个GIF来说明它应该如何工作。

服务类的目的
- 服务类的设计是为了将一个特定的操作与应用程序的其他部分分开,并隐藏其实现细节。
- 从本质上讲,将代码耦合到一个单一的功能上,会使修改变得困难和容易出错。在这种情况下,你将需要帮助。例如,存储服务是你所创建的一个新类的名称。它在内部如何运作,其他类并不知道。它就像调用服务的函数来存储和获取数据一样简单。
结论
使用服务类,你能够在你的应用程序中隐藏第三方代码的实现细节。然而,如果你需要在你的代码库中多次联系一个API方法,这一点尤其适用。
总而言之,使用API封装类来隐藏实现细节,包括API方法的所有输入和输出参数,并设计一个简单的抽象服务类来轻松改变另一个版本。