flutter學習三:實現登錄界面,登錄成功後跳轉到首頁,從我的界面退出登錄

效果圖依次如下:

             

 

                       

           

 

一步一步摸索、查找資源,最後實現登錄界面,登錄成功後跳轉到首頁,從我的界面退出登錄,這個過程涉及到了:Widgets的使用(TextField、RaisedButton、SizedBox、Row……)、路由、交互……等相關知識,代碼有相應的註釋(有些是根據自己的理解來寫,會有不太貼切的地方,諒解一下),

完整代碼如下:

1.main.dar

import 'package:flutter/material.dart';
import 'package:flutter_app/screen/Home.dart';
import 'package:flutter_app/screen/LoginScreen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {

      return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),

      routes: {
        /**
         * 命名導航路由,啓動程序默認打開的是以'/'對應的界面LoginScreen()
         * 凡是後面使用Navigator.of(context).pushNamed('/Home'),都會跳轉到Home(),
         */
        '/': (BuildContext context) => new LoginScreen(),
        '/Home': (BuildContext context) => new Home(),     
      }, 
     }
}  

2.1 LoginScreen.dart(類似LoginActivity、login.xml)

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class LoginScreen extends StatefulWidget {
  @override
  State<LoginScreen> createState() {
    return new _LoginScreenState();
  }
}

class _LoginScreenState extends State<LoginScreen> {
  //用於登錄時判斷輸入的賬號、密碼是否符合要求
  static bool _accountState, _passwordState = false;

  //提示語
  static String _checkHint;

  //監聽賬號輸入框的文字變化
  static TextEditingController _accountController = new TextEditingController();

  //監聽密碼輸入框的文字變化
  static TextEditingController _passwordController =
      new TextEditingController();

//用於路由(就是界面的跳轉),當跳轉的事件沒有寫在build裏面時用到(我這裏抽到了loginButton裏面)
  static BuildContext context1;

  //校驗賬號是否符合條件
  static void _checkAccount() {
    //校驗賬號不爲空且長度大於7(自定義校驗條件)
    if (_accountController.text.isNotEmpty &&
        _accountController.text.trim().length > 7) {
      _accountState = true;
    } else {
      _accountState = false;
    }
  }

  //校驗密碼是否符合條件
  static void _checkPassword() {
    //校驗密碼不爲空且長度大於8小於等於15(自定義校驗條件)
    if (_passwordController.text.isNotEmpty &&
        _passwordController.text.length > 8 &&
        _passwordController.text.length <= 15) {
      _passwordState = true;
    } else {
      _passwordState = false;
    }
  }

   //賬號輸入框樣式
  static Widget buildAccountTextFied(TextEditingController controller) {
    /**
     *需要定製一下某些顏色時返回Theme,不需要時返回TextField(如後面的密碼)
     * 修改輸入框顏色:沒有獲取焦點時爲hintColor,獲取焦點後爲:primaryColor
     */
    return Theme(
      data: new ThemeData(
          primaryColor: Colors.amber, hintColor: Colors.greenAccent),
      child: new TextField(
        //鍵盤的樣式
        keyboardType: TextInputType.text,
        //監聽
        controller: controller,
        //最大長度
        maxLength: 30,
        //顏色跟hintColor
        //最大行數
        maxLines: 1,
        //是否自動更正
        autocorrect: true,
        //是否自動化對焦
        autofocus: false,
        //是否是密碼格式(輸入的內容不可見)
        obscureText: false,
        //文本對齊方式
        textAlign: TextAlign.start,
        //輸入文本的樣式
        style: TextStyle(fontSize: 20, color: Colors.black),
        //允許輸入的格式(digitsOnly數字)
        inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
        //內容改變回調
        onChanged: (account) {
          print('change $account');
        },
        //提交觸發回調
        onSubmitted: (account) {
          print('submit $account');
        },
        //是否禁用
        enabled: true,
        decoration: InputDecoration(
            fillColor: Colors.blue[50],
            //底色
            filled: true,
            //有聚焦,labelText就會縮小到輸入框左上角,顏色primaryColor,沒聚焦前顏色跟hintColor
            labelText: '賬號',
            //聚焦時才顯示,顏色跟hintColor
            hintText: '請輸入賬號',
            //紅色
//            errorText: '輸入錯誤',
            //紅色,現在在輸入框的左下角,跟errorText位置一樣(優先顯示errorText)
//            helperText: 'acount',
            //輸入框內左側,有聚焦,顏色跟primaryColor
            prefixIcon: Icon(Icons.person),
            //輸入框左側的widget(可是text、icon……)
            icon: Text(
              '賬號:',
              style: TextStyle(fontSize: 20, color: Colors.black),
            ),
            //輸入框內右側的widget
            suffixIcon: Icon(Icons.account_circle),
//            有聚焦顯示顏色跟hintColor,顯示在輸入框的右邊
            suffixText: "後綴",
            contentPadding: EdgeInsets.all(5),
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(21.11), //邊框裁剪成11.11°角
              borderSide: BorderSide(
                  color: Colors.black,
                  width: 25.0), //邊框顏色、大小沒有效果,所以使用返回的是Theme,
            )),
      ),
    );
  }

  //密碼輸入框樣式
  static Widget buildPasswordTextFied(TextEditingController controller) {
    return TextField(
      //鍵盤的樣式
      keyboardType: TextInputType.number,
      //監聽
      controller: controller,
      //最大長度
      maxLength: 30,
      //顏色跟hintColor
      //最大行數
      maxLines: 1,
      //是否自動更正
      autocorrect: true,
      //是否自動化對焦
      autofocus: false,
      //是否是密碼格式(輸入的內容不可見)
      obscureText: true,
      //文本對齊方式
      textAlign: TextAlign.start,
      //輸入文本的樣式
      style: TextStyle(fontSize: 20, color: Colors.black),
      //允許輸入的格式(digitsOnly數字)
      inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
      //內容改變回調
      onChanged: (password) {
        print('change $password');
      },
      //提交觸發回調
      onSubmitted: (password) {
        print('submit $password');
      },
      //是否禁用
      enabled: true,
      decoration: InputDecoration(
          //底色配合filled:true纔有效
          fillColor: Colors.blue[50],
          filled: true,
          //輸入聚焦以後,labelText就會縮小到輸入框左上角,紅色,沒聚焦前顏色跟hintColor
          labelText: '密碼',
          //聚焦時才顯示,顏色跟hintColor
          hintText: '請輸入密碼',
          //紅色
//          errorText: '輸入錯誤',
          //紅色,現在在輸入框的左下角,跟errorText位置一樣(優先顯示errorText)
//          helperText: 'password',
          //輸入框內左側widget,輸入聚焦時,顏色跟primaryColor
          prefixIcon: Icon(Icons.lock),
          //輸入框左側的widget(可是text、icon……)
          icon: Text(
            '密碼:',
            style: TextStyle(fontSize: 20, color: Colors.black),
          ),
          //輸入框內右側的widget
          suffixIcon: Icon(Icons.remove_red_eye),
          //聚焦時才顯示顏色跟hintColor,顯示在輸入框的右邊
          suffixText: '後綴',
          contentPadding: EdgeInsets.all(5),
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(21.11), //邊框裁剪成11.11°角
            borderSide: BorderSide(
                color: Colors.black, width: 25.0), //沒有效果,想要修改就返回Theme(如前面賬號樣式)
          )),
    );
  }

  //賬號、密碼輸入框
  Widget textSection = new Container(
    padding: const EdgeInsets.all(32.0),
    child: new Column(
      //主軸Flex的值
      mainAxisSize: MainAxisSize.max,
      //MainAxisAlignment:主軸方向上的對齊方式,會對child的位置起作用
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        buildAccountTextFied(_accountController),
        buildPasswordTextFied(_passwordController),
      ],
    ),
  );

  //登錄進行佈局及“賬號”、“密碼”格式校驗、彈窗的提示、路由(寫在build裏面太長了,抽出來)
  Widget loginButton = new Container(
      margin: const EdgeInsets.only(left: 35, right: 35),//這個widget距離父控件左右35(還有個all就是距離左上右下四個方向)
      child: new SizedBox(
          //用來設置寬高,如直接使用RaisedButton則不能設置
          height: 50,
          child: new RaisedButton(//一個凸起的材質矩形按鈕
              color: Colors.red,
              child: new Text(
                '登錄',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
              onPressed: () {//按下時的事件
                _checkAccount();//校驗賬號格式,以此來更新_accountState
                _checkPassword();//校驗賬號格式,以此來更新_passwordState
                if (_accountState) {
                  if (_passwordState) {
                    _checkHint =
                        '恭喜賬號:' + _accountController.text.toString() + "登錄成功";
                  } else {
                    _checkHint = '請輸入8~15位密碼!';
                  }
                } else {
                  _checkHint = '請輸入不低於7位賬號!';
                }
                showDialog(
                  context: context1,
                  barrierDismissible: true, //點擊彈窗外部是否消失
                  child: new AlertDialog(
                    title: new Text(//標題
                      '提示',
                      style:
                          new TextStyle(color: Colors.red[300], fontSize: 18),
                    ),
                    content: new Text(_checkHint),//提示語
                    actions: <Widget>[
                      new FlatButton(//一個扁平的Material按鈕
                          onPressed: () {
                            Navigator.of(context1).pop();//彈窗消失
                          },
                          child: Text('取消')),
                      new FlatButton(
                          //對話框按鈕
                          onPressed: () {
                            if (_accountState && _passwordState) {//賬號密碼都符合條件
                              Navigator.pushNamed(
                                  context1, '/Home'); //使用的是“命名導航路由”,具體去哪個界面,看main.dart 對應routeName('/Home')的界面
                            } else {
                              Navigator.of(context1).pop();//彈窗消失
                            }
                          },
                          child: Text('確定')),
                    ],
                  ),
                );
              })));

  @override
  Widget build(BuildContext context) {
    context1 = context;
    return Scaffold(
        appBar: new AppBar(
          title: new Text('登錄'),
        ),
        body: new ListView(
          children: [
            new Image.asset(
              'images/lake.jpg',
              width: 600,
              height: 240,
              //cover(充滿容器)、fill(充滿父容器)、contain(總有寬或高跟父一樣)、none(原圖居中顯示)、fitWidth(寬度跟父一樣)、fitHeight(高度跟父一樣)
              fit: BoxFit.contain,
            ),
            textSection,
            loginButton,
          ],
        ));
  }
}

2.2圖片資源的添加

  • 根目錄建立images文件夾,把準備好的lake.jpg圖片放進去,如下圖

  • pubspec.yaml,增加的圖片資源,需要加進去,如下:
    flutter:
      assets:
        - images/lake.jpg

3.Home.dart(類似MainActivity、main.xml,用來切換fragment)

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/screen/HomePage.dart';
import 'package:flutter_app/screen/MineScreen.dart';


class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _HomeState();
  }
}

class _HomeState extends State<Home> {
  int _currentIndex = 0;
  final List<Widget> _children = [
    new HomeScreen(),//首頁界面
    new MineScreen(),//我的界面
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _children[_currentIndex],
      bottomNavigationBar: new BottomNavigationBar(
        onTap: onTabTapped, //點擊切換
        currentIndex: _currentIndex,
        items: [
          new BottomNavigationBarItem(
            icon: new Icon(Icons.home),
            title: new Text('首頁'),
          ),
          new BottomNavigationBarItem(
            icon: new Icon(Icons.person),
            title: new Text('我的'),
          ),
        ],
      ),
    );
  }

  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }
}

4.HomeScreen.dart(類似HomeFragment、homefragment.xml)

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('首頁'),
      ),
      body: new Center(
        child: new Text(
            '我是Home界面',
          style: TextStyle(color: Colors.red, fontSize: 20),
        )
      ),
    );
  }
}

5.MineScreen.dart((類似MineFragment、minefragment.xml))

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class MineScreen extends StatelessWidget {
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('我的'),
      ),
      body: new Center(
        child: new RaisedButton(
            child: new Text('退出登錄'),
            onPressed: () {
              Navigator.of(context).pushNamed('/');//跳轉到main.dart對routeName('/')的界面
            }),
      ),
    );
  }
}

 

 剛剛想到一個問題:當你實現上面的效果後,回顧看一下“賬號”、“密碼”TextField重複寫了,增加了代碼量,我們是不是可以封裝一下,傳參數過去進行分別實現想要的效果,eg:obscureText(是否是密碼格式),賬號時傳個false、密碼時傳個true。

其它的也是同樣的情況,同樣的佈局,可以抽出來,進行調用即可,自己動手去實現一下哈,我就不重新貼代碼了。後期如果有優化或者補充的,我會繼續完善博文的。

 

補充:

1. 2020.3.18,經過後面的學習,可以優化底部導航欄,自定義選中、未選中的icon、text顏色,看效果圖、代碼:

                                          

 

//底部導航欄
class MainNavigator extends StatefulWidget {
  @override
  _MainNavigatorState createState() => new _MainNavigatorState();
}

class _MainNavigatorState extends State<MainNavigator> {
  final _defaultColor = Colors.grey;//沒有選中時icon、文字的顏色
  final _activeColor = Colors.red;//選中的icon、文字的顏色
  int _currentIndex = 0;//默認顯示第一個

  final List<Widget> _children = [
    new HomePage(),
    new DancePage(),
    new MinePage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _children[_currentIndex],
      bottomNavigationBar: new BottomNavigationBar(
        onTap: onTabTapped, //點擊切換
        currentIndex: _currentIndex,//當前顯示的界面下標
        type: BottomNavigationBarType.fixed, //文字的顯示,shifting選中時顯示,fixed不選中也顯示
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home, color: _defaultColor), //未選中的icon顏色
            activeIcon: Icon(Icons.home, color: _activeColor), //選中時的icon顏色
            title: new Text(
              '首頁',
              style: TextStyle(
                  color: _currentIndex != 0
                      ? _defaultColor
                      : _activeColor //不爲o表示未選中
                  ),
            ),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.directions_run, color: _defaultColor),
            activeIcon: Icon(Icons.directions_run, color: _activeColor),
            title: Text(
              '舞蹈',
              style: TextStyle(
                  color: _currentIndex != 1
                      ? _defaultColor
                      : _activeColor //不爲1表示未選中
                  ),
            ),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person, color: _defaultColor),
            activeIcon: Icon(Icons.person, color: _activeColor),
            title: Text(
              '我的',
              style: TextStyle(
                  color: _currentIndex != 2
                      ? _defaultColor
                      : _activeColor //不爲2表示未選中
                  ),
            ),
          ),
        ],
      ),
    );
  }

  //點擊切換時,更新當前選中的界面下標
  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }
}

 

 

上一篇:flutter學習二:親測實現官網構建佈局第一個例子完整代碼 

下一篇:flutter學習四:使用自定義字體 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章