Flutter项目之登录注册功能实现

目录:

1、页面效果


dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_haoke/pages/home/info/index.dart';
import 'package:flutter_haoke/pages/home/tab_profile/adverWidget.dart';
import 'package:flutter_haoke/pages/home/tab_profile/function_button.dart';
import 'package:flutter_haoke/pages/home/tab_profile/header.dart';

class TabProfile extends StatelessWidget {
  const TabProfile({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        title: Text("我的"),
        actions: [
          IconButton(
              onPressed: () {
                Navigator.of(context).pushNamed("setting");
              },
              icon: Icon(Icons.settings))
        ],
      ),
      body: ListView(
        children: [
          Header(),
          FunctionButton(),
          AdverWidget(),
          //引用5中的最新资讯
          Info(
            showTitle: true,
          )
        ],
      ),
    );
  }
}

2、登录两种状态界面

dart 复制代码
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter_haoke/scopoed_model/auth_model.dart';
import 'package:flutter_haoke/utils/scopoed_mode_helper.dart';

var loginRegisterStyle = TextStyle(color: Colors.white, fontSize: 18);

class Header extends StatelessWidget {
  const Header({Key? key}) : super(key: key);

  Widget _notloginBuild(BuildContext context) {
    return Container(
      height: 100,
      decoration: BoxDecoration(color: Colors.green),
      padding: EdgeInsets.only(left: 10, top: 20, bottom: 20),
      child: Row(
        children: [
          Container(
              margin: EdgeInsets.only(right: 10),
              width: 65,
              height: 65,
              child: CircleAvatar(
                backgroundImage: NetworkImage(
                    "https://tva1.sinaimg.cn/large/006y8mN6ly1g6tbgbqv2nj30i20i2wen.jpg"),
              )),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // Padding(padding: EdgeInsets.only(top: 10)),
              Row(
                children: [
                  GestureDetector(
                    onTap: () {
                      Navigator.of(context).pushNamed("login");
                    },
                    child: Text(
                      "登录",
                      style: loginRegisterStyle,
                    ),
                  ),
                  Text(
                    "/",
                    style: loginRegisterStyle,
                  ),
                  GestureDetector(
                    onTap: () {
                      Navigator.of(context).pushNamed("register");
                    },
                    child: Text(
                      "注册",
                      style: loginRegisterStyle,
                    ),
                  ),
                ],
              ),
              Text(
                "登录后获取更多体验",
                style: TextStyle(color: Colors.white),
              )
            ],
          )
        ],
      ),
    );
  }

  Widget _loginBuild(BuildContext context) {
    var userInfo =
        ScopoedModelHelper.getModel<AuthModel>(context).userInfo;
    String userName ="张三";
    if (userInfo !=null && userInfo.name !=null) userName =userInfo.name;
    String userImage =
        "https://tva1.sinaimg.cn/large/006y8mN6ly1g6tbnovh8jj30hr0hrq3l.jpg";
    if (userInfo !=null && userInfo.avator !=null) userImage =userInfo.avator;
    return Container(
      height: 100,
      decoration: BoxDecoration(color: Colors.green),
      padding: EdgeInsets.only(left: 10, top: 20, bottom: 20),
      child: Row(
        children: [
          Container(
              margin: EdgeInsets.only(right: 10),
              width: 65,
              height: 65,
              child: CircleAvatar(
                backgroundImage: NetworkImage(userImage),
              )),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // Padding(padding: EdgeInsets.only(top: 10)),
              Text(
                userName,
                style: loginRegisterStyle,
              ),
              Text(
                "查看编辑个人信息",
                style: TextStyle(color: Colors.white),
              )
            ],
          )
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    //从本地存储AuthModel中获取登录态
    var islogin = ScopoedModelHelper.getModel<AuthModel>(context).isLogin;
    print(islogin.toString());
    return islogin ? _loginBuild(context) : _notloginBuild(context);
  }
}

3、中间按钮部分

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_haoke/pages/home/tab_profile/function_button_data.dart';
import 'package:flutter_haoke/pages/home/tab_profile/function_button_widget.dart';

class FunctionButton extends StatelessWidget {
  const FunctionButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Wrap(
          spacing: 1,
          runSpacing: 1,
          children: list.map((item) {
            return FunctionButtonWidget(item);
          }).toList()),
    );
  }
}
dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_haoke/pages/home/tab_profile/function_button_data.dart';

class FunctionButtonWidget extends StatelessWidget {
  final FunctionButtonItem data;

  const FunctionButtonWidget(
    this.data, {
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        data.onTapHandle(context);
      },
      child: Container(
        margin: EdgeInsets.only(top: 30),
        width: MediaQuery.of(context).size.width * 0.33,
        child: Column(
          children: [Image.asset(data.imageUrl), Text(data.title)],
        ),
      ),
    );
  }
}

4、广告区域

dart 复制代码
import 'package:flutter/material.dart';

class AdverWidget extends StatelessWidget {
  const AdverWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(left: 10, right: 10, top: 30, bottom: 20),
      child: Image.network(
          "https://tva1.sinaimg.cn/large/006y8mN6ly1g6te62n8f4j30j603vgou.jpg"),
    );
  }
}

5、最新资讯

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_haoke/pages/home/info/data.dart';
import 'package:flutter_haoke/pages/home/info/index_widget.dart';

class Info extends StatelessWidget {
  final bool showTitle;
  final List<InfoItem> dataList;

  const Info({Key? key, this.showTitle = false, this.dataList = infoData})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: [
          if (showTitle)
            Container(
              padding: EdgeInsets.all(10),
              alignment: Alignment.centerLeft,
              child: Text(
                "更多资讯",
                style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.w600),
              ),
            ),
          Column(
            children: dataList.map((e) {
              return InfoItemWidget(e);
            }).toList(),
          )
        ],
      ),
    );
  }
}

6、登录注册页联调

6.1、网络请求工具类

dart 复制代码
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_haoke/config.dart';
import 'package:flutter_haoke/routes.dart';
import 'package:flutter_haoke/scopoed_model/auth_model.dart';
import 'package:flutter_haoke/utils/common_toast.dart';
import 'package:flutter_haoke/utils/scopoed_mode_helper.dart';

class DioHttp {
  late Dio _client;
  late BuildContext context;

  static DioHttp of(BuildContext context) {
    return DioHttp._innernal(context);
  }

  DioHttp._innernal(BuildContext context) {
    this.context = context;
    var options = BaseOptions(
        baseUrl: Config.BaseUrl,
        connectTimeout: 1000 * 10, //请求超时时间:10秒
        receiveTimeout: 1000 * 3,
        extra: {'context': context});
    Interceptor interceptor =
        InterceptorsWrapper(onResponse: (response, handler) {
      if (response == null) return handler.next(response);
      var code = json.decode(response.toString())['code'];
      if (code == 404) {
        CommontToast.showToast("地址路径错误");
        return handler.next(response);
      }
      if (code.toString().startsWith('4')) {
        //退出登录
        ScopoedModelHelper.getModel<AuthModel>(context).logout();
        //登录过期时如果是启动页不用跳转到登录页
        if (ModalRoute.of(context)!.settings.name == Routes.loading) {
          return handler.next(response);
        }
        CommontToast.showToast("登录过期!");
        Navigator.of(context).pushNamed(Routes.login); 
      }
      return handler.next(response);
    });
    _client = Dio(options);
    _client.interceptors.add(interceptor);
    this._client = _client;
    // if (_client == null || context != this.context) {
    //   this.context = context;
    //   var options = BaseOptions(
    //       baseUrl: Config.BaseUrl,
    //       connectTimeout: 1000 * 10, //请求超时时间:10秒
    //       receiveTimeout: 1000 * 3,
    //       extra: {'context': context});
    //   _client = Dio(options);
    //   this._client = _client;
    // }
  }

  Future<Response<Map<String, dynamic>>> get(String path,
      [Map<String, dynamic>? params, String? token]) async {
    var options = Options(headers: {'Authorization': token});
    return await _client.get(path, queryParameters: params, options: options);
  }

  Future<Response<Map<String, dynamic>>> post(String path,
      [Map<String, dynamic>? params, String? token]) async {
    var options = Options(headers: {'Authorization': token});
    return await _client.post(path, data: params, options: options);
  }

  Future<Response<Map<String, dynamic>>> postFormData(String path,
      [dynamic params, String? token]) async {
    var options = Options(
        contentType: 'multipart/form-data', headers: {'Authorization': token});
    return await _client.post(path, data: params, options: options);
  }
}

6.2、注册页联调

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_haoke/utils/common_toast.dart';
import 'package:flutter_haoke/utils/dio_http.dart';
import 'package:flutter_haoke/utils/string_is_bull_or_empty.dart';

class RegisterPage extends StatefulWidget {
  const RegisterPage({Key? key}) : super(key: key);

  @override
  State<RegisterPage> createState() => _RegisterPageState();
}

class _RegisterPageState extends State<RegisterPage> {
  //定义三个TextEditingController与用户名,密码,确认密码绑定
  var usernameController = TextEditingController();
  var passwordController = TextEditingController();
  var surePsdController = TextEditingController();

  _onRegisterHandle() async {
    var username = usernameController.text;
    var password = passwordController.text;
    var surePsd = surePsdController.text;
    if (password != surePsd) {
      return CommontToast.showToast("两次输入的密码不一致!");
    }
    if (StringIsNullOrEmpty(username) || StringIsNullOrEmpty(password)) {
      return CommontToast.showToast("用户名或密码不能为空!");
    }
    const url = "/register";
    var params = {'username': username, 'password': password};
    //请求后端注册接口存用户信息
    var res = await DioHttp.of(context).post(url, params);
//  {"data":{"code":0,"data":[{"name":"admin","ps":123}],"message":"success"}}
    if (res.data!["data"]["code"] == 0) {
      //存储成功后跳转到登录页进行登录
      CommontToast.showToast(res.data!["data"]["message"]);
      Navigator.of(context).pushReplacementNamed("login");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("注册"),
        ),
        body: SafeArea(
          minimum: EdgeInsets.all(30),
          child: ListView(
            children: [
              TextField(
               //进行绑定
                controller: usernameController,
                decoration: InputDecoration(
                  labelText: "用户名",
                ),
              ),
              Padding(padding: EdgeInsets.all(10)),
              TextField(
               //进行绑定
                controller: passwordController,
                obscureText: true,
                decoration: InputDecoration(
                  labelText: "密码",
                ),
              ),
              Padding(padding: EdgeInsets.all(10)),
              TextField(
               //进行绑定
                controller: surePsdController,
                obscureText: true,
                decoration: InputDecoration(
                  labelText: "确认密码",
                ),
              ),
              Padding(padding: EdgeInsets.all(10)),
              ElevatedButton(
                child: Text("注册"),
                //点击后执行注册的逻辑处理
                onPressed: () {
                  _onRegisterHandle();
                },
              ),
              Padding(padding: EdgeInsets.all(10)),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text("已有账号,"),
                  TextButton(
                      onPressed: () {
                        Navigator.pushReplacementNamed(context, "register");
                      },
                      child: Text("去登录~"))
                ],
              )
            ],
          ),
        ));
  }
}

6.3、登录问题分析


6.4、本地缓存

dart 复制代码
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

enum StoreKeys {
  token,
  city
}

class Store {
  static late StoreKeys storeKeys;
  final SharedPreferences _store;
  static Future<Store> getInstance() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return Store._internal(prefs);
  }

  Store._internal(this._store);

  getString(StoreKeys key) async {
    return _store.get(key.toString());
  }

  setString(StoreKeys key, String value) async {
    return _store.setString(key.toString(), value);
  }

  getStringList(StoreKeys key) async {
    return _store.getStringList(key.toString());
  }

  setStringList(StoreKeys key, List<String> value) async {
    return _store.setStringList(key.toString(), value);
  }
}

6.5、共享token

dart 复制代码
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_haoke/utils/dio_http.dart';
import 'package:flutter_haoke/utils/model/user_info.dart';
import 'package:flutter_haoke/utils/store.dart';
import 'package:flutter_haoke/utils/string_is_bull_or_empty.dart';
import 'package:scoped_model/scoped_model.dart';

class AuthModel extends Model {
  String _token = '';
  UserInfo? _userinfo;

  String get token {
    return _token;
  }

  UserInfo? get userInfo => _userinfo;

  bool get isLogin {
    if (_token.length > 0 && _token != null) return true;
    return false;
  }

  void initApp(BuildContext context) async {
    Store store = await Store.getInstance();
    String token = await store.getString(StoreKeys.token);
    if (!StringIsNullOrEmpty(token)) {
      //调用登录
      login(token, context);
    }
  }

  _getUserInfo(BuildContext context) async {
    var url = '/user';
    var res = DioHttp.of(context).get(url, {}, _token);
    //真实数据
    // var resMap = json.decode(res.toString());
    // var data = resMap['body'];
    // var userInfo = UserInfo.fromJson(data);
    //伪造数据
    var userInfo = UserInfo.fromJson({
      'name': "王哈哈",
      'avator':
          "https://img2.baidu.com/it/u=1274080955,1358023663&fm=26&fmt=auto",
      'id': '100'
    });
    _userinfo = userInfo;
    notifyListeners();
  }

  void login(String token, BuildContext context) {
    _token = token;
    notifyListeners();
    _getUserInfo(context);
  }

  void logout() async {
    _token = '';
    _userinfo = null;
    Store store = await Store.getInstance();
    store.setString(StoreKeys.token, _token);
    notifyListeners();
    print("删除token了");
  }
}
dart 复制代码
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:flutter_haoke/pages/home/index.dart';
import 'package:flutter_haoke/pages/loading.dart';
import 'package:flutter_haoke/routes.dart';
import 'package:flutter_haoke/scopoed_model/auth_model.dart';
import 'package:flutter_haoke/scopoed_model/city.dart';
import 'package:flutter_haoke/scopoed_model/room_filter.dart';
import 'package:scoped_model/scoped_model.dart';

class Application extends StatelessWidget {
  const Application({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) 
    //配置使用上路由router
    FluroRouter router = FluroRouter();
    Routes.configureRoutes(router);
     //配置使用上本地缓存ScopedModel
    return ScopedModel<CityModel>(
        model: CityModel(),
        child: ScopedModel<AuthModel>(
        //使用了两个model,这里配置的model都是全局的
          model: AuthModel(),
          child: ScopedModel<FilterBarModel>(
            model: FilterBarModel(),
            child: MaterialApp(
              theme: ThemeData(primarySwatch: Colors.green),
              onGenerateRoute: router.generator,
              // home: HomePage(),
              initialRoute: Routes.loading,
            ),
          ),
        ));
    ;
  }
}

6.6、登录页联调

dart 复制代码
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_haoke/routes.dart';
import 'package:flutter_haoke/scopoed_model/auth_model.dart';
import 'package:flutter_haoke/utils/common_toast.dart';
import 'package:flutter_haoke/utils/dio_http.dart';
import 'package:flutter_haoke/utils/scopoed_mode_helper.dart';
import 'package:flutter_haoke/utils/store.dart';
import 'package:flutter_haoke/utils/string_is_bull_or_empty.dart';
import 'package:flutter_haoke/widget/page_content.dart';

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

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

class _LoginPageState extends State<LoginPage> {
  bool showPassword = false;
  var usernameController = TextEditingController();
  var passwordController = TextEditingController();

  _onloginHandler() async {
    var username = usernameController.text;
    var password = passwordController.text;
    if (StringIsNullOrEmpty(username) || StringIsNullOrEmpty(password)) {
      CommontToast.showToast("用户名或密码不能为空!");
      return;
    }
    var url = '/user/login';
    var params = {'username': username, 'password': password};
    
    var res = await DioHttp.of(context).post(url, params);

    print(res);
    //伪造假数据模拟登录成功
    CommontToast.showToast('登录成功');
    String token = '123';
    //获取缓存变量
    Store store = await Store.getInstance();
    //存入token
    await store.setString(StoreKeys.token, token);
    //更新全局model
    ScopoedModelHelper.getModel<AuthModel>(context).login(token, context);
    //这个地方登录成功后为啥跳转到首页,因为项目启动默认是去首页,未登录才跳到登录页,登录成功后跳上一个页面就是到首页
    Timer(Duration(seconds: 1), () {
    //跳转到路由堆栈的上一个页面
      Navigator.of(context).pop();
    });

    //真实运行模式
    // if (res.data!['data']['code'] == 0) {

    //   CommontToast.showToast(res.data!['data']['message']);
    //   String token = res.data!['data']['body'];
    //   //获取缓存变量
    //   Store store = await Store.getInstance();
    //   //存入token
    //   await store.setString(StoreKeys.token, token);
    //   //更新全局model
    //   ScopoedModelHelper.getModel<AuthModel>(context).login(token, context);
    //   Timer(Duration(seconds: 1), () {
    //     Navigator.of(context).pop();
    //   });
    // }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("登录"),
        ),
        body: SafeArea(
          minimum: EdgeInsets.all(30),
          child: ListView(
            children: [
              TextField(
                controller: usernameController,
                decoration: InputDecoration(
                  labelText: "用户名",
                ),
              ),
              Padding(padding: EdgeInsets.all(10)),
              TextField(
                controller: passwordController,
                obscureText: !showPassword,
                decoration: InputDecoration(
                    labelText: "密码",
                    suffixIcon: IconButton(
                        onPressed: () {
                          setState(() {
                            showPassword = !showPassword;
                          });
                        },
                        icon: showPassword
                            ? Icon(Icons.visibility)
                            : Icon(Icons.visibility_off))),
              ),
              Padding(padding: EdgeInsets.all(10)),
              ElevatedButton(
                child: Text("登录"),
                onPressed: () {
                  _onloginHandler();
                },
              ),
              Padding(padding: EdgeInsets.all(10)),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text("还没有账号,"),
                  TextButton(
                      onPressed: () {
                        Navigator.pushReplacementNamed(
                            context, Routes.register);
                      },
                      child: Text("去注册~"))
                ],
              )
            ],
          ),
        ));
  }
}

6.7、退出登录

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_haoke/scopoed_model/auth_model.dart';
import 'package:flutter_haoke/utils/common_toast.dart';
import 'package:flutter_haoke/utils/scopoed_mode_helper.dart';

class SettingPage extends StatelessWidget {
  const SettingPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("设置")),
      body: TextButton(
          onPressed: () {
            ScopoedModelHelper.getModel<AuthModel>(context).logout();
            CommontToast.showToast("退出登录");
          },
          child: Text("退出登录")),
    );
  }
}
相关推荐
故事不长丨19 小时前
C#正则表达式完全攻略:从基础到实战的全场景应用指南
开发语言·正则表达式·c#·regex
源心锁19 小时前
👋 手搓 gzip 实现的文件分块压缩上传
前端·javascript
哈库纳玛塔塔19 小时前
放弃 MyBatis,拥抱新一代 Java 数据访问库
java·开发语言·数据库·mybatis·orm·dbvisitor
phltxy20 小时前
从零入门JavaScript:基础语法全解析
开发语言·javascript
Kagol20 小时前
JavaScript 中的 sort 排序问题
前端·javascript
天“码”行空20 小时前
java面向对象的三大特性之一多态
java·开发语言·jvm
cos21 小时前
Fork 主题如何更新?基于 Ink 构建主题更新 CLI 工具
前端·javascript·git
odoo中国21 小时前
Odoo 19 模块结构概述
开发语言·python·module·odoo·核心组件·py文件按
代码N年归来仍是新手村成员1 天前
【Java转Go】即时通信系统代码分析(一)基础Server 构建
java·开发语言·golang
奋斗的小青年!!1 天前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙