1. 程式人生 > >Flutter開發之Widget佈局和頁面導航

Flutter開發之Widget佈局和頁面導航

一、水平佈局Row

Row控制元件可以分為非靈活排列和靈活排列兩種,靈活的可以在外邊加入Expanded使用

兩者混用:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      title: '',
      home: new Scaffold(
        appBar: new AppBar(title: new Text('hello row')),
        body: new Row(
          children: <Widget>[
            Expanded(                      //靈活使用
                child: new RaisedButton(
                  onPressed: () {},
                  color: Colors.blue,
                  child: new Text('Blue Button'),
                )),
            new RaisedButton(
              onPressed: () {},
              color: Colors.green,
              child: new Text('Green Button'),
            ),
          ],
        ),
      ),
    );
  }
}

二、垂直佈局Column

      對齊方式:

  • main軸: 比如Row元件,那水平就是主軸。比如Column元件,那垂直就是主軸。

  • cross軸:比如Row元件,那垂直就是副軸。比如Columu元件,那水平就是副軸。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      title: '',
      home: new Scaffold(
        appBar: new AppBar(title: new Text('hello Column')),
        body: new Center(
          child: new Column(
            crossAxisAlignment: CrossAxisAlignment.center, //副軸對齊方式
            mainAxisAlignment: MainAxisAlignment.center, //主軸對齊方式
            children: <Widget>[
              new Text('君不見黃河之水天上來,',
                  style: TextStyle(
                    color: Colors.black,
                    fontSize: 30.0,
                  )),
              new Text('東流到海不復還,',
                  style: TextStyle(
                    color: Colors.redAccent,
                    fontSize: 30.0,
                  )),
            ],
          ),
        ),
      ),
    );
  }
}

三、層疊佈局Stack

alignment屬性:控制層疊的位置

alignment: const FractionalOffset(dx,dy) dx、dy 為0~1
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var stack = new Stack(
      alignment: const FractionalOffset(0.5, 0.8),
      children: <Widget>[
        new CircleAvatar(
          backgroundImage:
              new NetworkImage('https://profile.csdnimg.cn/0/5/2/1_jyd0124'),
          radius: 100.0,
        ),
        new Container(
          decoration: BoxDecoration(
            color: Colors.cyan,
          ),
          child: new Text('blog_jyd0124'),
          padding: EdgeInsets.all(5.0),
        ),
      ],
    );
    // TODO: implement build
    return MaterialApp(
      title: '',
      home: new Scaffold(
        appBar: new AppBar(title: new Text('hello Stack')),
        body: new Center(child: stack),
      ),
    );
  }
}

說明:CircleAvatar元件經常用來作頭像,radius屬性可以設定圖片的弧度

Stack佈局高階用法:Positioned(層疊定位元件)用於層疊多個元件

var stack = new Stack(
      //alignment: const FractionalOffset(0.5, 0.8),
      children: <Widget>[
        new CircleAvatar(
          backgroundImage:
              new NetworkImage('https://profile.csdnimg.cn/0/5/2/1_jyd0124'),
          radius: 100.0,
        ),
        new Positioned(
            bottom: 20.0,
            left: 60.0,
            child: new Container(
              decoration: BoxDecoration(
                color: Colors.cyan,
              ),
              child: new Text('blog_jyd0124'),
              padding: EdgeInsets.all(5.0),
            )),
      ],
    );

四、卡片佈局Card

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var card = new Card(
      child: new Column(
        children: <Widget>[
          ListTile(
            title: new Text('成都市',style:TextStyle(fontWeight:FontWeight.w100)),
            subtitle: new Text('QQ:150048****'),
            leading: new Icon(Icons.account_balance,color: Colors.blue,),
          ),
          ListTile(
            title: new Text('西安市',style:TextStyle(fontWeight:FontWeight.w100)),
            subtitle: new Text('QQ:150048****'),
            leading: new Icon(Icons.account_balance,color: Colors.blue,),
          ),
          ListTile(
            title: new Text('蘭州市',style:TextStyle(fontWeight:FontWeight.w100)),
            subtitle: new Text('QQ:150048****'),
            leading: new Icon(Icons.account_balance,color: Colors.blue,),
          ),
        ],
      )
    );
    // TODO: implement build
    return MaterialApp(
      title: '',
      home: new Scaffold(
        appBar: new AppBar(title: new Text('hello Column')),
        body: new Center(child: card),
      ),
    );
  }
}

 五、頁面的導航和返回

       1.RaisedButton按鈕元件

          兩個常用屬性:

  • child:可以放入容器,圖示,文字
  • onPressed:事件的響應,一般呼叫Navigator元件

       2.Navigator元件

  • Navigator.push()   =====跳轉到下一個頁面,接受兩個引數一個是上下文context,另一個是要跳轉的函式。
  • Navigator.pop()     =====返回到上一個頁面,接受一個context(上下文)引數
import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
      title: '導航演示',
      home: new FirstScreen(),
    ));

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text(''),
      ),
      body: Center(
        child: RaisedButton(
            child: Text('跳轉'),
            onPressed: () {
              Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => new SecondScreen(),
                  ));
            }),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      body: new Scaffold(
        appBar: new AppBar(title: new Text('跳轉完成')),
        body: new Center(
            child: RaisedButton(
          child: Text('返回'),
          onPressed: () {
            Navigator.pop(context);
          },
        )),
      ),
    );
  }
}

 六、導航引數的傳遞和接收

import 'package:flutter/material.dart';

class Product {
  String title;
  String description;
  Product(this.title, this.description);
}

void main() {
  runApp(MaterialApp(
    title: '導航的資料傳遞和接受',
    home: ProductList(
        products:
            List.generate(20, (i) => Product('商品 $i', '這是一個商品詳情,編號為 $i'))),
  ));
}

class ProductList extends StatelessWidget {
  final List<Product> products;
  ProductList({Key key, @required this.products}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('商品列表'),
      ),
      body: ListView.builder(
          itemCount: products.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(products[index].title),
              onTap: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) =>
                            ProductDetail(product: products[index])));
              },
            );
          }),
    );
  }
}

class ProductDetail extends StatelessWidget {
  final Product product;
  ProductDetail({Key key, @required this.product}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('${product.title}')),
      body: Center(
        child: Text('${product.description}'),
      ),
    );
  }
}
  • 小技巧 :Android Studio 中輸入stless可以自動生成StatelessWidget,但在VSCode中,需先安裝Awesome Flutter snippets外掛,然後輸入stlss;

七、頁面跳轉並返回資料

  1.非同步請求與等待

           使用async...await

  2.SnackBar

           顯示提示資訊的一個控制元件,會自動隱藏,SnackBar是以Scaffold的showSnackBar()方法來進行顯示的;

  3.返回資料

           Navigator.pop()帶第二個引數就可以

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: '頁面跳轉返回資料',
    home: FirstPage(),
  ));
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('得到一個數字')),
      body: Center(
        child: RouteButton(),
      ),
    );
  }
}

class RouteButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      onPressed: () {
        navigateToNumber(context);
      },
      child: Text('Get'),
    );
  }

  navigateToNumber(BuildContext context) async {
    //async是啟用非同步方法
    final result = await Navigator.push(
        //等待
        context,
        MaterialPageRoute(builder: (context) => Number()));
    Scaffold.of(context).showSnackBar(SnackBar(content: Text('$result')));
  }
}

class Number extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('我是你找的數')),
      body: Center(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text('兩位數'),
              onPressed: () {
                Navigator.pop(context, '兩位數:98');
              },
            ),
            RaisedButton(
              child: Text('三位數'),
              onPressed: () {
                Navigator.pop(context, '三位數:124');
              },
            ),
          ],
        ),
      ),
    );
  }
}

 八、靜態資源和專案圖片的處理

    在pubspec.yaml檔案中宣告資原始檔

    測試:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Image.asset('images/csdn.jpg'),
    );
  }
}

  九、客戶端打包(Android)

 1. 配置APP的圖片的目錄    /android/app/src/main/res/

 2.配置APP的名稱、圖示和系統許可權的目錄    /android/app/src/main/AndroidManifest.xml

  

 3.生成keystore

       <1> flutter doctor -v  命令找到keytool.exe的位置

        <2>cd進入這個目錄,然後執行下面命令就會在D盤下面有一個jks的檔案

  keytool -genkey -v -keystore D:\key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

          <3>到專案目錄下的android資料夾下,建立一個名為key.properties的檔案,並開啟貼上下面的程式碼

storePassword=<password from previous step>    //輸入上一步建立KEY時輸入的 金鑰庫 密碼
keyPassword=<password from previous step>    //輸入上一步建立KEY時輸入的 金鑰 密碼
keyAlias=key
storeFile=<E:/key.jks>    //key.jks的存放路徑

4.配置key註冊

     <1>進入專案目錄的/android/app/build.gradle檔案,在android{這一行前面,加入如下程式碼

def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

    <2>把如下程式碼進行替換

 替換成的程式碼:

signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile file(keystoreProperties['storeFile'])
        storePassword keystoreProperties['storePassword']
    }
}
buildTypes {
    release {
        signingConfig signingConfigs.release
    }
}

 5.生成apk

   在終端輸入 flutter build apk,這時候打包成功,可在build\app\outputs\apk\release\找到

   然後在終端輸入flutter install 直接安裝到