Flutter 自定義列表以及本地圖片引用

zxRisingSun發表於2021-01-21

 

前言


 

     上篇關於Flutter的文章總結了下標籤+導航的專案模式的搭建,具體的有需要的可以去看看Flutter分類的文章,這篇文章我們簡單的總結一下關於Flutter本地檔案引用以及簡單的自定義List的使用,我們先總結本地圖片的引用。今天這篇文章的具體的UI效果如下:

 

 

引用本地圖片


  

      我們沒有使用到的我們暫時先不提,等後面慢慢補充進去,比如說網路圖片的顯示等等,我們現總結一下關於本地圖片的使用,具體的我們需要下面幾步:

      1、建立檔案匯入資源

      Flutter的檔案資源需要我們建立一個檔案去管理,我們可以定義一個images的檔案,當然這個名字不是固定的但需要留意下它的檔案等級,它裡面還可以裝別的其他資原始檔,你要叫Resource也可以的。然後不管是做Android的還是iOS的都知道我們的圖片資源是分2x和3x的,所以我們在你建立的檔案下面再建立一個2.0x和3.0x的資料夾分別存放相應倍數的圖片資源。如下圖:

 

 

      2、不是說直接匯入就能直接使用的,還需要處理一下 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

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

  #### 圖片資源
  assets:
    - images/icon_header.png
    - images/icon_heath.png
    - images/icon_village.png
    - images/mine_train.png

      前面我們說的資料夾名字不是固定的,繫結使用的的就是assets,這裡其實你理解成圖片在專案中的層級位置就可以了。看上面Flutter給的註釋資訊,我們完全可以在匯入別的資訊,如 fonts等等。

      3、現在可以直接使用了,它的使用還是相對比較簡單的,我們直接上程式碼,需要注意的點是使用的時候需要的是圖片全程,記得帶上字尾。

Image.asset(
     "images/icon_header.png",
     width: 20,
     height: 20,
 )

 

實現上面我的頁面


 

      上面的實現我們需要簡單的瞭解幾個相應的控制元件,我們一個一個的介紹一下先,最後就可以出來我們前面的我的頁面的UI了。

      1、InkWell  它是一個效果控制元件 ,點選有潑墨的水波的效果 ,經常和Material+顏色透明一起使用。

      2、Row 水平元件,首先一點是不管是我們現在說的Row還是我們後面說的 Column只能在繼承與StatelessWidget或者StatefullWidget的Widget中使用。

Row({
    Key key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline textBaseline = TextBaseline.alphabetic,
    List<Widget> children = const <Widget>[],
  }) : super(
    children: children,
    key: key,
    direction: Axis.horizontal,
    mainAxisAlignment: mainAxisAlignment,
    mainAxisSize: mainAxisSize,
    crossAxisAlignment: crossAxisAlignment,
    textDirection: textDirection,
    verticalDirection: verticalDirection,
    textBaseline: textBaseline,
);

      它就這麼一個構造方法,還是相對比較簡單的,首先它是不能滾動的,但是它可以靈活佈局,如果要讓某個子元件填充滿剩餘剩餘空間,可以在 children 中使用 Expanded 元件,children 這個屬性看上面的原始碼我們知道,它是一個 Widget 陣列,這個我們在後面會使用到。具體的它裡面的 Expanded 元件我們下面總結,現不在這裡細說。

      它裡面的屬性還是相對比較簡單的,可以自己瞭解學習一下。我們接著看下面的元件。

      3、Column

      上面說的是水平的,那這個肯定就是豎直的了,其實它倆挺像的,也都是最基礎的。我們把它初始化方法放出來對比一下,對比一下前面的Row:

Column({
    Key key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline textBaseline,
    List<Widget> children = const <Widget>[],
  }) : super(
    children: children,
    key: key,
    direction: Axis.vertical,
    mainAxisAlignment: mainAxisAlignment,
    mainAxisSize: mainAxisSize,
    crossAxisAlignment: crossAxisAlignment,
    textDirection: textDirection,
    verticalDirection: verticalDirection,
    textBaseline: textBaseline,
  );
}

      4、Expanded

      這個我們也需要說說的,因為我們的Row和Column都需要Expanded,其實它也是比較簡單的:

class Expanded extends Flexible {
  /// Creates a widget that expands a child of a [Row], [Column], or [Flex]
  /// so that the child fills the available space along the flex widget's
  /// main axis.
  const Expanded({
    Key key,
    int flex = 1,
    @required Widget child,
  }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}

      我們就需要了解兩個引數一個 flex 和一個 child,child我們就不多說了,比較簡單,flex 按照一個比例理解,比如兩個Expanded , 並排需要控制它們的寬度比例,就可以使用這個屬性。這些我們在下面的程式碼使用中都有體現的。我們程式碼中細說。

      我們看具體的程式碼,理解了上面大概說的,這個程式碼就相對比較簡單了,我們先看看頂部的頭部我們是怎樣定義的:

class MineHeader extends StatelessWidget {

  String userHeaderImage;
  String userName;

  /// 這裡定義了就可以在外面使用這個方法進行初始化
  MineHeader(this.userHeaderImage, this.userName);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.orange,
      height: 100,

      /// 水平佈局
      /// 在Row中使用Expanded的時候,無法指定Expanded中的子元件的寬度width,但可以指定其高度height。
      /// 同理,在Column中使用Expanded的時候,
      /// 無法指定Expanded中的子元件的高度height,可以指定寬度width。
      child: Row(
        children: <Widget>[
          Expanded(
            flex: 1,
            child: Container(
              padding: EdgeInsets.only(left: 15),
              child: Image.asset(
                userHeaderImage,
                width: 50,
                height: 50,
              ),
            ),
          ),
          Expanded(
            flex: 5,
            child: Container(
              padding: EdgeInsets.only(left: 15),
              child: Text(
                userName,
                /// 18號 藍色 加粗
                style: TextStyle(
                    fontSize: 18,
                    color: Colors.white,
                    fontWeight: FontWeight.bold),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

      需要我們注意的點我們都在上面程式碼註釋中基本上都說了,接下來我們我們看看下面列表的程式碼:

class MineItemWidget extends StatelessWidget {
  String imageName;
  String title;

  @required
  VoidCallback onTap;

  MineItemWidget(this.imageName, this.title, {this.onTap});

  @override
  Widget build(BuildContext context) {
    return Container(
      /// 子檢視 顏色容器
      child: Column(
        children: <Widget>[
          ///
          Container(
            height: 53,
            child: _mineItem(imageName, title),
          ),
          Container(
            color: Color(0xffeaeaea),
            constraints: BoxConstraints.expand(height: 1.0),
          ),
        ],
      ),
    );
  }

  Widget _mineItem(String imageName, String title) {
    return InkWell(
      onTap: () {
        this.onTap();
      },
      child: Row(
        ///
        children: <Widget>[
          Expanded(
            flex: 1,
            child: Container(
              padding: EdgeInsets.only(left: 15),
              child: Image.asset(
                imageName,
                width: 20,
                height: 20,
              ),
            ),
          ),
          Expanded(
            flex: 6,
            child: Container(
              padding: EdgeInsets.only(left: 15),
              child: Text(
                title,
                style: TextStyle(fontSize: 16),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

      最後就是把它倆寫到我們的 MinePage ,MinePage的程式碼如下:

class MinePage extends StatefulWidget {
  MinePage({Key key}) : super(key: key);
  @override
  _MinePageState createState() => _MinePageState();
}

class _MinePageState extends State<MinePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: Text("我的"),
        backgroundColor: Colors.orange,
        elevation: 0, // 去掉Appbar底部陰影
      ),
      body: ListView(
        children: <Widget>[
          /// 頭部的View
          MineHeader("images/icon_header.png", "張旭旭旭旭啊"),
          /// 線
          _listViewLine,
          MineItemWidget("images/icon_heath.png", "我的健康", onTap: () {
            /// 跳轉到檢視按鈕內容介面
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => MineCustomButton()),
            );
          }),
          MineItemWidget("images/icon_village.png", "我的旅行", onTap: () {
            print('我的旅行');
          }),
          _listViewLine,
          MineItemWidget("images/mine_train.png", "我的家鄉", onTap: () {
            print('我的家鄉');
          }),
        ],
      ),
    );
  }

  /// 分割線
  Widget get _listViewLine {
    return Container(
      color: Color(0xffeaeaea),
      height: 6,
    );
  }
}

  

      這樣整個一個基本的效果就出來了,如最開始我們給的那張圖效果一樣。打算在後面一篇文章中再說說我們常見到一些元件,按鈕,輸入框,進度條等等的。

      參考文章:

      1、 Flutter ListView 自定義

      2、詳解flutter中本地資源圖片的使用

      3、專案地址

 

相關文章