學(xué)習(xí)Flutter的第二天(Scaffold)

2. Scaffold

2.1 AppBar

名稱 功能
leading 標(biāo)題前置控件。在首頁通常顯示應(yīng)用程序的Logo,其它頁面通常顯示為返回按鈕
title 頁面標(biāo)題。通常顯示當(dāng)前頁面的標(biāo)題文字,可以放組件
actions 標(biāo)題后置控件。通常使用IconButton來表示,可以放按鈕組
bottom 底部控件。通常用tabBar來表示放置Tab標(biāo)簽欄
backgroundColor 導(dǎo)航背景顏色
iconTheme 圖標(biāo)樣式
textTheme 文字樣式
centerTitle 標(biāo)題是否居中顯示
elevation 值是0.0 就是沒有陰影
import 'package:flutter/material.dart';
import './pages/tabs.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(primarySwatch: Colors.blue),
      home: Scaffold(
        appBar: AppBar(
          // 背景顏色
          backgroundColor: Colors.red,
          // 左側(cè)按鈕
          leading: IconButton(
              onPressed: () {
                print("左側(cè)按鈕");
              },
              icon: const Icon(Icons.menu)),
          // 右邊按鈕
          actions: [
            IconButton(
                onPressed: () {
                  print("setting");
                },
                icon: const Icon(Icons.settings)),
            IconButton(
                onPressed: () {
                  print("more");
                },
                icon: const Icon(Icons.more))
          ],
          title: const Text("Demo1"),
        ),
        body: const Text("sss"),
      ),
    );
  }
}

2.2 TabBar

TabBar是作為AppBar的一部分存在。參考文檔 中提供了兩種實(shí)現(xiàn)頂部TabLayout的方法,兩者效果是一樣的,但第二種功能更為豐富。具體為 TabBar + TabBarView兩個(gè)weight結(jié)合。

名稱 功能
tabs 標(biāo)簽組
controller 標(biāo)簽控制器
isScrollable 標(biāo)簽組是否可以滾動(dòng)
indicatorColor 指示器的顏色
indicatorWeight 指示器高度
indicatorPadding 指示器的內(nèi)邊距
indicator 指標(biāo)器裝飾
indicatorSize 指示器的大小
labelColor 標(biāo)簽的顏色
labelStyle 標(biāo)簽的樣式
labelPadding 標(biāo)簽的內(nèi)邊距
unselectedLabelColor 選中標(biāo)簽的顏色
unselectedLabelStyle 選中標(biāo)簽的樣式

2.2.1 DefaultTabController

使用 DefaultTabController作為最外層控制器,聯(lián)調(diào) TabBar + TabBarView,這種方式簡單方便,但致命缺點(diǎn)是拿不到當(dāng)前選中tab的index。

class FirstPage extends StatefulWidget {
  FirstPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  State<StatefulWidget> createState() => _FirstPageState();
}

/// DefaultTabController (TabBar + TabBarView) 使用 (內(nèi)部還是TabController實(shí)現(xiàn))
class _FirstPageState extends State<FirstPage> {
  final List<Tab> myTabs = <Tab>[
    Tab(text: '全部訂單'),
    Tab(text: '已完成'),
    Tab(text: '未完成')
  ];

  @override
  Widget build(BuildContext context) {
    // 1. 使用 DefaultTabController 作為外層控制器
    return DefaultTabController(
      length: myTabs.length,// 定義tab數(shù)量
      child: Scaffold(
        appBar: AppBar(
          title: Text('FirstPage'),
          
          // 2. 使用 TabBar
          bottom: TabBar(
            tabs: myTabs // 定義TabWeight,若數(shù)量和定義不一致會(huì)報(bào)錯(cuò)
          ),
        ),
        
        // 3. 使用 TabBarView
        body: TabBarView(
            children: <Widget>[
        
              /// 全部訂單
              Center(child: Text('全部訂單')),
        
              /// 已完成訂單
              Center(child: Text('已完成')),
        
              /// 未完成訂單
              Center(child: Text('未完成'))
            ]),
      ),
    );
  }
}

2.2.2 TabController

實(shí)現(xiàn)SingleTickerProviderStateMixin,重寫初始化和注銷方法,利用控制器TabController管理和更多操作,其實(shí)第一種方法內(nèi)部也是由TabController實(shí)現(xiàn)的。

// 1. 實(shí)現(xiàn) SingleTickerProviderStateMixin 
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  TabController _tabController;
  
  static const List<Tab> _homeTopTabList = <Tab>[
    Tab(text: '音樂', icon: Icon(Icons.music_note)),
    Tab(text: '體育', icon: Icon(Icons.directions_run)),
    Tab(text: '天氣', icon: Icon(Icons.cloud_queue)),
    Tab(text: '科技', icon: Icon(Icons.toys))
  ];

  // 2. 初始化狀態(tài)
  @override
  void initState() {
    super.initState();
    // TabController的滾動(dòng)事件會(huì)觸發(fā)一次監(jiān)聽, 點(diǎn)擊事件會(huì)觸發(fā)兩次監(jiān)聽(一次是正常觸發(fā),一次是tab的動(dòng)畫觸發(fā))
    _tabController = TabController(length: _homeTopTabList.length, vsync: this);
    
    // 添加監(jiān)聽獲取tab選中下標(biāo)
    _tabController.addListener((){
      _currentTopTabIndex = _tabController.index;
    });
  }
  
  // 3. 注銷狀態(tài)
  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
          
          // 4. 添加TabBar
          bottom: TabBar(
            tabs: _homeTopTabList,
            controller: _tabController
          ),
        ),

        // 5. 添加TabBarView
        body: TabBarView(
          controller: _tabController,
          children: <Widget>[
            _tabMusic(context),
            _tabSport(context),
            _tabSport(context),
            _tabSport(context)
          ],
        ),
       );
  }

注:關(guān)于TabController 的監(jiān)聽有個(gè)坑 踩坑TabBar之TabController.addListener

2.1 bottomNavigationBar

BottomNavigationBarBottomNavigationBarItem 配合來共同展示Flutter里面的底部狀態(tài)欄,底部狀態(tài)欄是在移動(dòng)端很重要的控件。

名稱 功能
items 底部導(dǎo)航按鈕集合
iconSize icon
currentIndex 默認(rèn)選中第幾個(gè)
onTap 選中變化回調(diào)函數(shù)
fixedColor 選中的顏色
type BottomNavigationBarType.fixed 或 BottomNavigationBarType.shifting

下面代碼實(shí)現(xiàn):點(diǎn)擊底部的導(dǎo)航按鈕,顯示不同的頁面

main.dart

import 'package:flutter/material.dart';
import './pages/tabs.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const Tabs(),
    );
  }
}

tabs.dart

import 'package:flutter/material.dart';
import './tabs/home.dart';
import './tabs/category.dart';
import './tabs/me.dart';

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

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  final List<Widget> _pages = const [Home(), Category(), Me()];

  int _currentIndex = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Demo1"),
      ),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "首頁"),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: "分類"),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: "我"),
        ],
        // 底部有4個(gè),或者4個(gè)以上的時(shí)候才生效
        type: BottomNavigationBarType.fixed,
      ),
    );
  }
}

category.dart 、home.dart 、 me.dart

import 'package:flutter/material.dart';

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

  @override
  State<Category> createState() => _CategoryState();
}

class _CategoryState extends State<Category> {
  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text("分類"),
    );
  }
}

2.2 floatingActionButton

用來實(shí)現(xiàn)懸浮按鈕效果的

class ScffoldHomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ScffoldHomePageState();
  }
}

class ScffoldHomePageState extends State<ScffoldHomePage> {

  ///當(dāng)前選中的頁面
  num index =0;

  @override
  Widget build(BuildContext context) {
    ///使用 Scaffold 組件來構(gòu)建應(yīng)用的基本頁面
    /// 頁面的架構(gòu)
    return Scaffold(
      ///定義頁面的標(biāo)題
      appBar: AppBar(
        title: Text("這里是首頁"),
      ),
      ///定義的頁面的主體內(nèi)容
      body:pageWidgetList[index],
      ///定義的懸浮按鈕
      floatingActionButton: FloatingActionButton(
        child: Text("++"),
        ///點(diǎn)擊響應(yīng)事
        onPressed: () {
          print("點(diǎn)擊了 FloatingActionButton");
        },
        ///長按提示
        tooltip: "點(diǎn)擊了 tooltip s ",
        ///設(shè)置懸浮按鈕的背景
        backgroundColor: Colors.red,
        ///獲取焦點(diǎn)時(shí)顯示的顏色
        focusColor: Colors.green,
        ///鼠標(biāo)懸浮在按鈕上時(shí)顯示的顏色
        hoverColor: Colors.yellow,
        ///水波紋顏色
        splashColor: Colors.deepPurple,
        ///定義前景色 主要影響文字的顏色
        foregroundColor: Colors.black,
        ///配制陰影高度 未點(diǎn)擊時(shí)
        elevation: 0.0,
        ///配制陰影高度 點(diǎn)擊時(shí)
        highlightElevation: 20.0,
      ),
      ///用來控制  FloatingActionButton 的位置
      ///FloatingActionButtonLocation.endFloat 默認(rèn)使用 浮動(dòng)右下角
      ///FloatingActionButtonLocation.endDocked 右下角
      ///FloatingActionButtonLocation.endTop  右上角
      ///FloatingActionButtonLocation.startTop 左上角
      ///FloatingActionButtonLocation.centerFloat 底部中間浮動(dòng)
      ///FloatingActionButtonLocation.centerDocked 底部中間不浮動(dòng)
      floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
    );
  }
}

2.3 drawer

抽屜組件

import "package:flutter/material.dart";
 
 
class CategoryPage extends StatefulWidget {
    CategoryPage({Key key}) : super(key: key);
    _CategoryPageState createState() => _CategoryPageState();
}
 
class _CategoryPageState extends State<CategoryPage> {
    @override
    Widget build(BuildContext context) {
 
        return Scaffold(
            appBar: AppBar(
                title: Text("Flutter App"), 
            ),
            // 左側(cè)抽屜
            drawer: Drawer(
                child: Text('左側(cè)抽屜'), 
            ),
            // 右側(cè)抽屜
            endDrawer: Drawer(
                child: Text('右側(cè)抽屜'), 
            ), 
        );
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容