Flutter 入門指北(Part 2)之基礎部件

該文已受權公衆號 「碼個蛋」,轉載請指明出處android

上一節介紹了 Dart 的一些語法,以及配置環境的網址,這節咱們就能夠開始瞭解下 Fluttergit

主要包括 MaterialAppScaffoldTextImageIconButton 以及 AppBar 部份內容,準備出發~github

Flutter runApp

新建 flutter 項目後,能夠看到 lib 下的 main.dart 中 void main() => runApp(MyApp()); 這句就是程序的入口了。這裏能夠簡單看下源碼數據庫

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame();
}

///....
static WidgetsBinding ensureInitialized() {
  if (WidgetsBinding.instance == null)
    WidgetsFlutterBinding();
  return WidgetsBinding.instance;
}

///....
void attachRootWidget(Widget rootWidget) {
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget
  ).attachToRenderTree(buildOwner, renderViewElement);
}
複製代碼

首先會建立一個 WidgetsBinding 單例對象,而後把傳入的 App 添加到 rootWidget 中,scheduleWarmUpFrame 方法比較長,這邊看下對該方法的註釋第一句就能瞭解方法的主要功能了markdown

Schedule a frame to run as soon as possible網絡

「安排框架儘快運行起來」(原諒我這渣英語,只能看懂不會翻譯..大概就是「快速啓動框架」的意思吧)app

Flutter App

接着看下 MyApp 這個類,繼承自 StatelessWidget 並在 build 方法返回一個 MaterialApp 實例,(偷偷講下,其實這邊還能夠返回 CupertinoApp,這是一個 iOS 風格的 widget,基本上你看到部件帶 「Cupertino」的都是 iOS 風格的 widget,這裏先不講 iOS 風格的部件,目前 flutter 對 Cupertino 系列的 widget 支持不是很好,包括部件的廣度,多語言的支持等等方面都不是很友好,因此咱們仍是繼續看 MD 風格的 Android 部件吧~),這裏先看下 MaterialApp 的構造函數,介紹一些經常使用的參數框架

const MaterialApp({
    Key key,
    this.navigatorKey,
    this.home, // 主界面的內容 widget
    this.routes = const <String, WidgetBuilder>{}, // 帶 router 和路由跳轉有關
    this.initialRoute,
    this.onGenerateRoute,
    this.onUnknownRoute,
    this.navigatorObservers = const <NavigatorObserver>[], 
    this.builder,
    this.title = '', // *相似標題
    this.onGenerateTitle, // 主要用於多語言狀況下,須要根據當前語言替換 title,須要使用該值
    this.color, // 主題色,若是該值未設置,取 theme.primaryColor,未設置 theme 則取藍色
    this.theme, // App 的主題風格,包括主題色,按鈕默認顏色等等
    this.locale, // 帶 locale 的和多語言適配相關
    this.localizationsDelegates,
    this.localeListResolutionCallback,
    this.localeResolutionCallback,
    this.supportedLocales = const <Locale>[Locale('en', 'US')],
    this.debugShowMaterialGrid = false, 
    this.showPerformanceOverlay = false,
    this.checkerboardRasterCacheImages = false,
    this.checkerboardOffscreenLayers = false,
    this.showSemanticsDebugger = false,
    this.debugShowCheckedModeBanner = true, // debug 模式下,是否顯示 DEBUG 標示橫幅
  })
複製代碼

MaterialApp 繼承自 StatefulWidget,它和 MyApp 所繼承的類 StatelessWidget,就是平常開發中,自定義部件一般繼承的抽象類了。less

  1. StatelessWidget 是狀態不可變部件,經過其構建的部件通常用來展現固定內容,例如須要展現固定的功能按鈕列表,不須要根據不一樣界面狀態進行修改其展現內容
  2. StatefulWidget 是可改變狀態的部件,好比咱們須要經過網絡或者數據庫獲取數據,而後修改部件鎖展現的數據內容,則須要經過 StatefulWidget 來構建。固然,不是說 StatelessWidget 不能實現修改界面數據的功能,這就須要涉及到 狀態管理 的概念了,後面有機會再講,這邊先埋坑【坑1】

Flutter Scaffold

進入 App 後就須要構建界面了,Flutter 提供了 Scaffold 來快速構建一個 MaterialDesign 風格的界面,仍是先看下 Scaffold 的構造函數吧,瞭解幾個比較經常使用的部分。ide

const Scaffold({
    Key key,
    this.appBar, // 界面頂部的那條欄,這邊須要返回一個 AppBar 實例
    this.body, // 界面的內容部分
    this.floatingActionButton, // 懸浮部分,能夠經過 floatingActionButtonLocation 設置位置
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer, // 側滑抽屜部分,從左側滑出(應該和語言有關,和文字方向同向)
    this.endDrawer, // 側滑抽屜部分,從右側滑出
    this.bottomNavigationBar, // 底部導航欄,就是一般看到的底部 TAB 切換部件
    this.bottomSheet, // 展現從底部彈出的,起到提示做用的,經過 showModalBottomSheet 展現
    this.backgroundColor, // 界面的背景色
    this.resizeToAvoidBottomPadding = true, // 避免 body 被底部彈出部件填充,例如輸入法鍵盤
    this.primary = true, // 當前的 Scaffold 是否須要被展現在屏幕最上層
  })
複製代碼

來張圖吧,簡潔明瞭

scaffold分佈.png

瞭解完 Scaffold 的總體構造後,咱們從上到下,經過構造函數來了解下各個 Widget 的使用方法

AppBar
AppBar({
    Key key,
    this.leading, // 用於設置 AppBar 前置的按鈕,例如設置返回咱們須要的返回按鈕等
    this.automaticallyImplyLeading = true, // 是否使用系統默認生成的按鈕,若是替換 leading 的默認按鈕,最好將該屬性設置成 false
    this.title, // AppBar 所須要展現的組件,傳入一個 Widget 實例,一般使用 Text 展現一個標題
    this.actions, // AppBar 末尾懸浮的一些操做組件,例如常見的會在末尾設置一個「...」按鈕,點擊彈出一個 menue 提供給用戶操做選擇
    this.flexibleSpace, // AppBar 的背景,能夠設置顏色,背景圖等等 
    this.bottom, // bottom 用於展現頂部導航 TAB
    this.elevation = 4.0,
    this.backgroundColor, // AppBar 的背景色,若是隻須要修改顏色,能夠不經過 flexibleSpace 修改
    this.brightness,
    this.iconTheme, // 按鈕的默認樣式
    this.textTheme, // 文字的默認樣式
    this.primary = true,
    this.centerTitle, // 是否將展現的 title 居中
    this.titleSpacing = NavigationToolbar.kMiddleSpacing, // AppBar title 兩側的空白間隔
    this.toolbarOpacity = 1.0,
    this.bottomOpacity = 1.0,
  })
複製代碼

在展現 AppBardemo 以前,咱們先學習幾個基本的組件 TextImageIconButton 分佈用於展現文字,圖片,圖標,按鈕

Text
const Text(this.data, { // Text 須要展現的文字
    Key key,
    this.style, // 文字的樣式,包括顏色,大小,間距等等屬性,這邊就不繼續展現 TextStyle 構造函數了,否則我怕你們都不想繼續看了,稍後經過例子來講明
    this.textAlign, // 文字的對齊方式,包括左對齊,右對齊,居中等,詳見 TextAlign 類
    this.textDirection, // 文字方向,ltr(left to right) 或者 rtl(right to left)
    this.locale, 
    this.softWrap, // 當文字一行顯示不完是否換行
    this.overflow, // 若是超出限制的行數,以哪一種方式省略未展現的內容
    this.textScaleFactor, // 文字縮放比例
    this.maxLines, // 最多展現的行數
    this.semanticsLabel,
  })
複製代碼

說了那麼多,相信不少小夥伴都要急着擼代碼了吧,接着來展現一些 Text 的示例,接下來的例子都會直接替換 HomePage 內的展現內容,其他都是相同的,接下來請關注 Text 別的部件先忽略,後面會介紹,這邊先埋坑【坑2】

import 'package:flutter/material.dart';

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

class DemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.lightBlue),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Container(
          padding: const EdgeInsets.only(top: 10.0),
          child: Center(
              child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('綠色背景黑色文字展現',
                  style: TextStyle(
                      color: Colors.black, // 設置文字顏色,不可和 foreground 同時設置
                      fontSize: 24.0, // 字體大小
                      letterSpacing: 2.0, // 每一個字符之間的間隔
                      background: Paint()..color = Colors.green)), // 背景色
              Text('這是一個帶紅色下劃線的文字展現',
                  style: TextStyle(
                      color: Colors.black,
                      fontSize: 24.0,
                      // 文字裝飾線,除了 underline 還有 overline, lineThrough,
                      // 不一樣的樣式小夥伴能夠經過本身修改代碼來查看
                      decoration: TextDecoration.underline,
                      // 文字裝飾線的類型,除了 solid 還有 double,dotted,dashed,wavy 可選
                      decorationStyle: TextDecorationStyle.solid,
                      // 裝飾線的顏色
                      decorationColor: Colors.red))
            ],
          )),
        ));
  }
}
複製代碼

該部分代碼查看源碼 text_main.dart 文件

最後的展現效果以下圖:

text 展現.png

Image

/壞笑 按照慣例,咱們仍是先看下 Image 的構造函數吧

const Image({
    Key key,
    // 一個 ImageProvider 實例,可是 ImageProvider 是一個抽象類,Flutter 已經給咱們提供以下
    // AssetImage,NetworkImage,FileImage,MemoryImage 這四種圖片加載器,爲了方便調用
    // 咱們能夠直接經過 Image.asset, Image.network, Image.file, Image.memory 簡化,
    // 經過方法名,能夠看出分別從 asset 文件,網絡,文件,內存中加載圖片
    @required this.image, 
    this.semanticLabel,
    this.excludeFromSemantics = false,
    this.width, // 圖片寬度
    this.height, // 圖片高度
    this.color, // 圖片背景色
    this.colorBlendMode, // color 和圖片的混合模式(這個值比較多,能夠一個個嘗試)
    this.fit, // 圖片填充方式 fill, cover, contain, fillWidth, fillHeight, scaleDown, none
    this.alignment = Alignment.center, // 對齊方式
    this.repeat = ImageRepeat.noRepeat, // 若未填充滿空間,重複展現的方式
    this.centerSlice,
    this.matchTextDirection = false,
    this.gaplessPlayback = false,
    this.filterQuality = FilterQuality.low,
  })
複製代碼

好了好了,我知道大家又想本身寫代碼嘗試下了,在這以前,須要你先準備一張本地圖片,而後在項目的根目錄,也就是 lib 文件夾同層,建立一個新的文件夾,命名爲 images,把你準備好的圖片放到這個目錄下。放好以後打開 pubspec.yaml 把圖片資源文件註冊下

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
 uses-material-design: true

  # 這邊註冊資源文件,之後有圖片文件也能夠只註冊 images 文件夾,會自動讀取內部的文件
 assets:
 - images/ali.jpg
複製代碼

註冊完成後,就能夠繼續愉快的擼代碼了~

class HomePage extends StatelessWidget {
  final String _assetAli = 'images/ali.jpg';
  final String _picUrl =
      'https://timg05.bdimg.com/timg?wapbaike&quality=60&size=b1440_952&cut_x=143&cut_y=0&cut_w=1633&'
      'cut_h=1080&sec=1349839550&di=cbbc175a45ccec5482ce2cff09a3ae34&'
      'src=http://imgsrc.baidu.com/baike/pic/item/4afbfbedab64034f104872baa7c379310b551d80.jpg';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Container(
          padding: const EdgeInsets.only(top: 10.0),
          child: Center(
              child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              // 這種展現圖片方式和下一種會有相同的效果
              Image(image: AssetImage(_assetAli), width: 80.0, height: 80.0),
              // 接下來加載圖片都會使用這些比較方便的方法
              Image.asset(_assetAli, width: 80.0, height: 80.0),
              // 加載一張網絡圖片
              Image.network(_picUrl,
                  height: 80.0,
                  // 橫向重複
                  repeat: ImageRepeat.repeatX,
                  // MediaQuery.of(context).size 獲取到的爲上層容器的寬高
                  width: MediaQuery.of(context).size.width),
              // 經過設置混合模式,能夠看到圖片展現的樣式已經修改
              Image.asset(_assetAli,
                  width: 80.0, height: 80.0, color: Colors.green, colorBlendMode: BlendMode.colorDodge),
              // 會優先加載指定的 asset 圖片,而後等網絡圖片讀取成功後加載網絡圖片,會經過漸隱漸現方式展示
              // cover 方式按照較小的邊佈滿,較大的給切割
              // contain 會按照最大的邊佈滿,較小的會被留白
              // fill 會把較大的一邊壓縮
              // fitHeight, fitWidth 分別按照長寬來佈滿
              FadeInImage.assetNetwork(
                  placeholder: _assetAli, image: _picUrl, width: 120.0, height: 120.0, fit: BoxFit.cover),
              // Icon 相對屬性少了不少,須要傳入一個 IconData 實例,flutter 提供了不少圖標,
              // 可是實際狀況咱們須要加入咱們本身的圖標,這邊再埋坑【坑3】
              // size 爲圖標顯示的大小,color 爲圖標的顏色,這邊經過 Theme 獲取主題色調
              Icon(Icons.android, size: 40.0, color: Theme.of(context).primaryColorDark)
            ],
          )),
        ));
  }
}
複製代碼

該部分代碼查看源碼 image_main.dart 文件

最後的效果以下:

Image 展現.png

Button

Flutter 提供了各類類型的 Button 幾乎是大同小異的,這邊就抽取一些比較經常使用的展現下效果,經常使用的主要有 RaisedButtonFlatButtonIconButtonOutlineButtonMaterialButtonFloatActionButtonFloatingActionButton.extended

Button 都有一個 onPress 參數,是 VoidCallback 類型的參數,經過查看源碼能夠知道 VoidCallback 是無參無返回值的一種類型參數。若是該參數傳入的值爲 null 那麼這個按鈕的就不可點擊狀態,無點擊效果,等會能夠在例子中查看。還有就是 child 參數,這裏就是傳入你須要展現的內容,好比 TextIcon 等等。別的參數基本能夠經過參數名瞭解,這邊不擴展了(再看源碼我怕大家都不想繼續看下去了...)

Talk is cheap, show me the code

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        padding: const EdgeInsets.only(top: 10.0),
        child: Center(
            child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              onPressed: () {
                print('This is a Rased Button can be clicked');
              },
              child: Text('Raised Enable'),
            ),
            RaisedButton(onPressed: null, child: Text('Raised Disable')),
            FlatButton(
              onPressed: () => print('This is a Flat Button can be clicker'),
              child: Text('Flat Enable'),
            ),
            FlatButton(onPressed: null, child: Text('Flat Disable')),
            IconButton(icon: Icon(Icons.android), onPressed: () {}),
            IconButton(icon: Icon(Icons.android), onPressed: null),
            MaterialButton(onPressed: () {}, child: Text('Material Enable')),
            MaterialButton(onPressed: null, child: Text('Material Disable')),
            OutlineButton(onPressed: () {}, child: Text('Outline Enable')),
            OutlineButton(onPressed: null, child: Text('Outline Enable')),
          ],
        )),
      ),
      floatingActionButton:
          FloatingActionButton.extended(onPressed: () {}, icon: Icon(Icons.android), label: Text('Android')),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    );
  }
}
複製代碼

該部分代碼查看源碼 button_main.dart 部分

最終的效果圖:

button 展現.png

這篇終於到末尾了,最後留了 3 個坑等之後解決

最後代碼的地址仍是要的:

  1. 文章中涉及的代碼:demos

  2. 基於郭神 cool weather 接口的一個項目,實現 BLoC 模式,實現狀態管理:flutter_weather

  3. 一個課程(當時買了想看下代碼規範的,代碼更新會比較慢,雖然是跟着課上的一些寫代碼,可是仍是作了本身的修改,不少地方看着不舒服,而後就改爲本身的實現方式了):flutter_shop

若是對你有幫助的話,記得給個 Star,先謝過,你的承認就是支持我繼續寫下去的動力~

相關文章
相關標籤/搜索