Flutter for OpenHarmony 登录认证小指南:用 Flutter 给鸿蒙 App 安上 “安全小锁”✨
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、写在前面:为什么要做登录认证呀?
哈喽哈喽~在鸿蒙 App 里,登录认证就像给自家房门装一把可爱的小锁,既能挡住不速之客,又能让我们的专属数据乖乖待在里面不跑丢~这次我用 Flutter 搭了一套超实用的登录功能,从粉粉的登录页面到稳稳的身份校验,一步步实现下来,感觉自己像给 App 穿了件安全小外套,安全感直接拉满!
这次的小项目里,我做了三件超重要的小事:
画了一个软乎乎的登录页面,用户名和密码输入框乖乖排排坐
用 dio 给后端发消息,让它帮我们检查身份是不是 “自己人”
用 Bloc 管着登录状态,让 App 随时知道我们是 “已登录的小可爱” 还是 “未登录的小客人”
接下来就跟我一起看看,怎么用 Flutter 给鸿蒙 App 安上这把安全小锁吧~
二、第一步:给 App 画个甜甜的登录页面🎨
登录页面可是用户和 App 打招呼的第一面,当然要做得又可爱又好用啦!我用 Flutter 搭了一个适配鸿蒙设备的登录界面,软圆角的输入框、粉粉的按钮,看着就心情好好~
页面里的小细节可不少哦:用户名和密码输入框都加了小图标,密码框还藏了个小眼睛按钮,页面用了 ScrollView 包起来,就算键盘弹出来,输入框也不会被挡住,超贴心~按钮在加载的时候会变成转圈的小圈圈,用户一看就知道 “哦,正在帮我登录呢”。
完整登录页面代码

dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'auth_bloc.dart';

void main() {
  runApp(const LoginApp());
}

class LoginApp extends StatelessWidget {
  const LoginApp({super.key});

  
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => AuthBloc(),
      child: MaterialApp(
        title: '鸿蒙登录认证',
        theme: ThemeData(primarySwatch: Colors.pink),
        home: const LoginPage(),
      ),
    );
  }
}

class LoginPage extends StatefulWidget {
  const LoginPage({super.key});

  
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();
  bool _passwordVisible = false;

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(24),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              const SizedBox(height: 80),
              const Text(
                '小可爱登录',
                style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 40),
              // 用户名输入框
              TextField(
                controller: _usernameController,
                decoration: const InputDecoration(
                  labelText: '用户名',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.person),
                ),
              ),
              const SizedBox(height: 20),
              // 密码输入框
              TextField(
                controller: _passwordController,
                obscureText: !_passwordVisible,
                decoration: InputDecoration(
                  labelText: '密码',
                  border: const OutlineInputBorder(),
                  prefixIcon: const Icon(Icons.lock),
                  suffixIcon: IconButton(
                    icon: Icon(_passwordVisible ? Icons.visibility : Icons.visibility_off),
                    onPressed: () {
                      setState(() {
                        _passwordVisible = !_passwordVisible;
                      });
                    },
                  ),
                ),
              ),
              const SizedBox(height: 40),
              // 登录按钮
              BlocBuilder<AuthBloc, AuthState>(
                builder: (context, state) {
                  return ElevatedButton(
                    onPressed: state is AuthLoading
                        ? null
                        : () {
                            context.read<AuthBloc>().add(
                                  LoginSubmitted(
                                    username: _usernameController.text,
                                    password: _passwordController.text,
                                  ),
                                );
                          },
                    style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 16)),
                    child: state is AuthLoading
                        ? const CircularProgressIndicator(color: Colors.white)
                        : const Text('马上登录', style: TextStyle(fontSize: 18)),
                  );
                },
              ),
              // 错误提示
              BlocBuilder<AuthBloc, AuthState>(
                builder: (context, state) {
                  if (state is AuthFailure) {
                    return Padding(
                      padding: const EdgeInsets.only(top: 20),
                      child: Text(state.errorMessage, style: const TextStyle(color: Colors.red), textAlign: TextAlign.center),
                    );
                  }
                  return const SizedBox();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

三、第二步:用 dio 发个 “身份小纸条”📨
画好页面,接下来就要让 App 和后端说悄悄话啦!我用 dio 写了一个登录请求的小工具,用户输入用户名和密码后,App 会把这些信息打包成一张 “身份小纸条”,发给后端让它帮忙检查~
这个小工具可聪明啦:会先帮我们检查用户名和密码有没有空着,给请求加了 10 秒的小闹钟,还会把请求过程打印出来,方便我们找问题~
dio 网络请求工具代码

dart
import 'package:dio/dio.dart';

class AuthService {
  final Dio _dio = Dio();
  final String baseUrl = 'https://api.example.com';

  AuthService() {
    _dio.options.baseUrl = baseUrl;
    _dio.options.connectTimeout = const Duration(seconds: 10);
    _dio.options.receiveTimeout = const Duration(seconds: 10);
    _dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
  }

  Future<Map<String, dynamic>> login(String username, String password) async {
    if (username.isEmpty || password.isEmpty) {
      return {'success': false, 'message': '用户名和密码都要填哦~'};
    }

    try {
      final res = await _dio.post('/auth/login', data: {'username': username, 'password': password});
      return {'success': true, 'data': res.data, 'message': '登录成功啦!'};
    } on DioException catch (e) {
      return {'success': false, 'message': e.response?.data['message'] ?? '网络不太好,再试一下吧~'};
    }
  }
}

四、第三步:用 Bloc 当 “状态小管家”🧸
登录之后,App 得知道我们是不是已经登录成功啦,不然打开别的页面,它都不知道我们是谁~这时候 Bloc 就像个贴心的小管家,帮我们管着登录状态呢!
Bloc 的工作流程超可爱的:用户点下登录按钮,Bloc 会收到一个 “登录请求” 的小事件,它会告诉 UI “我正在帮你登录哦”,等后端回复消息,再告诉 UI 成功还是失败~

Bloc 状态管理完整代码
dart
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'auth_service.dart';

// 事件
abstract class AuthEvent extends Equatable {
  const AuthEvent();
}

class LoginSubmitted extends AuthEvent {
  final String username;
  final String password;
  const LoginSubmitted({required this.username, required this.password});
  
  List<Object> get props => [username, password];
}

// 状态
abstract class AuthState extends Equatable {
  const AuthState();
}

class AuthInitial extends AuthState {
  
  List<Object> get props => [];
}

class AuthLoading extends AuthState {
  
  List<Object> get props => [];
}

class AuthSuccess extends AuthState {
  final Map<String, dynamic> userInfo;
  const AuthSuccess(this.userInfo);
  
  List<Object> get props => [userInfo];
}

class AuthFailure extends AuthState {
  final String errorMessage;
  const AuthFailure(this.errorMessage);
  
  List<Object> get props => [errorMessage];
}

// Bloc核心
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthService _service = AuthService();
  AuthBloc() : super(AuthInitial()) {
    on<LoginSubmitted>(_onLogin);
  }

  Future<void> _onLogin(LoginSubmitted event, Emitter<AuthState> emit) async {
    emit(AuthLoading());
    final result = await _service.login(event.username, event.password);
    if (result['success']) {
      emit(AuthSuccess(result['data']));
    } else {
      emit(AuthFailure(result['message']));
    }
  }
}

五、pubspec.yaml 依赖配置

yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.1.3
  equatable: ^2.0.5
  dio: ^5.4.0

这是我的运行截图:在这里插入图片描述

六、跑起来啦!看看效果怎么样?✨
把页面、请求和状态管家拼在一起,整个登录流程就跑起来啦!我在鸿蒙真机上试了好几次,过程超顺利:
输入正确的用户名和密码,按钮变成小圈圈转了两圈,就跳转到主页面啦
输错密码的时候,页面会弹出小提示,告诉我们哪里错了
断网的时候,App 会温柔地提醒我们 “网络好像不太好,检查一下再试试吧”
把 App 退到后台再打开,它还记得我是登录状态,不用重新输入信息
看着自己做的小功能在鸿蒙设备上乖乖跑起来,真的超有成就感!原来给 App 做登录认证,也可以这么简单又可爱~
七、最后想说的话💌
这次用 Flutter 给鸿蒙 App 做登录认证,就像搭了一个小小的安全城堡,页面是软软的城门,请求是递进去的身份纸条,Bloc 是守着城门的小管家,每一步都稳稳的。
其实 Flutter 和鸿蒙的适配比我想象中要友好很多,只要注意好权限配置、页面适配和状态管理,就能做出又好用又可爱的功能啦~如果你也想给自己的鸿蒙 App 加个登录功能,不妨试试这个小方法,说不定会有意外的小惊喜哦!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐