flutter精美UI - 1

1、效果圖

3.gif

2、本節(jié)的知識點

2-1、為靜態(tài)資源創(chuàng)建配置類

比如本節(jié)內(nèi)容每張圖片都有名字和描述,那我們統(tǒng)一配置一個類

class TravelBean {
  String name;//名字
  String location;//描述
  String url;//靜態(tài)資源的地址
  TravelBean(this.name, this.location, this.url);
  //我們可以添加靜態(tài)方法 返回指定的內(nèi)容
  static List <TravelBean > generateTravelBean(){
    return [
        TravelBean (*,*,*)、、、
    ]
  }
}

2-2、獲取手機屏幕劉海(即手機型號等狀態(tài)欄)的高度

有時候我們可能不會設(shè)置appBar,讓背景圖穿透AppBar
例如抖音的個人中心背景圖它是直接穿透狀態(tài)欄的,我們本次的詳情頁面也是穿透

MediaQuery.of(context).padding.top

2-3、獲取屏幕的寬度/高度

1、double.infinity //無限大
2、MediaQuery.of(context).size.width  /高度:.size.height

2-4、跨頁面動畫

hero.gif

本節(jié)效果的首頁頂部圖片切換路由至詳情頁的頂部動畫
使用Hero組件:注意此效果是跨頁面即跨路由

Hero源碼
const Hero({
    Key key,
    @required this.tag, //必選參數(shù)規(guī)定兩個頁面之間的Hero標(biāo)簽
    this.createRectTween,
    this.flightShuttleBuilder,
    this.placeholderBuilder,
    this.transitionOnUserGestures = false,//IOS滑動切換路由是否支持
    @required this.child,//子節(jié)點
  }) : assert(tag != null),
       assert(transitionOnUserGestures != null),
       assert(child != null),
       super(key: key);

具體使用方法可以查看Flutter進階:在應(yīng)用中實現(xiàn) Hero(飛行) 動畫

2-4、獲取AppBar的高度

kToolbarHeight

2-5、設(shè)置沉浸式導(dǎo)航欄

通常我們使用的都是material風(fēng)格的樣式,就會造成狀態(tài)這一塊有陰影

21

if (Platform.isAndroid) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
        statusBarColor: Colors.white,
        statusBarBrightness: Brightness.dark,
        statusBarIconBrightness: Brightness.dark));
  }

2-6、設(shè)置狀態(tài)欄字體黑白顏色

AnnotatedRegion<SystemUiOverlayStyle>(
      value: SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarIconBrightness: Brightness.dark,
        statusBarBrightness: Brightness.dark,
      )

2-7、本節(jié)的重點:CustomScrollView組件

CustomScrollView是可以使用Sliver來自定義滾動模型(效果)的組件。它可以包含多種滾動模型,舉個例子,假設(shè)有一個頁面,頂部需要一個GridView,底部需要一個ListView,而要求整個頁面的滑動效果是統(tǒng)一的,即它們看起來是一個整體。如果使用GridView+ListView來實現(xiàn)的話,就不能保證一致的滑動效果,因為它們的滾動效果是分離的,所以這時就需要一個"膠水",把這些彼此獨立的可滾動組件"粘"起來,而CustomScrollView的功能就相當(dāng)于“膠水”。

flutter中文網(wǎng)- CustomScrollView

3、源碼剖析

//HomePage

import 'package:flutter/material.dart';

import 'TravelBean.dart';
import 'DetailPage.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        actions: <Widget>[
          Padding(
            padding: EdgeInsets.only(right: 15),
            child: Icon(
              Icons.menu,
              color: Colors.black,
            ),
          )
        ],
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Padding(
            padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
            child: Text(
              "Travel Blog",
              style: TextStyle(
                color: Colors.black,
                fontSize: 30,
              ),
            ),
          ),
          Expanded(
            flex: 2,
            child: TravelWidget(),
          ),
          Padding(
            padding: EdgeInsets.symmetric(horizontal: 15, vertical: 15),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text(
                  "Most Popular",
                  style: TextStyle(
                    color: Colors.black,
                    fontSize: 20,
                  ),
                ),
                Text(
                  "View all",
                  style: TextStyle(
                    color: Colors.deepOrange,
                    fontSize: 20,
                  ),
                ),
              ],
            ),
          ),
          Expanded(
            flex: 1,
            child: MostPopularWidget(),
          )
        ],
      ),
    );
  }
}

class MostPopularWidget extends StatelessWidget {
  List<TravelBean> _list = TravelBean.generateMostPopularBean();

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      padding: EdgeInsets.symmetric(horizontal: 15),
      scrollDirection: Axis.horizontal,
      itemBuilder: (context, index) {
        var bean = _list[index];
        return GestureDetector(
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return DetailPage(bean);
            }));
          },
          child: Hero(
            tag: bean.url,
            child: Stack(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(bottom: 30, right: 10),
                  child: ClipRRect(
                    borderRadius: BorderRadius.circular(5),
                    child: Image.asset(
                      bean.url,
                      width: 170,
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
                Positioned(
                  bottom: 50,
                  left: 15,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Material(
                        color: Colors.transparent,
                        child: Text(
                          bean.location,
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 15,
                          ),
                        ),
                      ),
                      Material(
                        color: Colors.transparent,
                        child: Text(
                          bean.name,
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 15,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      },
      itemCount: _list.length,
    );
  }
}

class TravelWidget extends StatelessWidget {
  List<TravelBean> _list = TravelBean.generateTravelBean();

  @override
  Widget build(BuildContext context) {
    return PageView.builder(
      controller: PageController(viewportFraction: 0.9),
      itemBuilder: (context, index) {
        var bean = _list[index];
        return GestureDetector(
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return DetailPage(bean);
            }));
          },
          child: Hero(
            tag: bean.url,
            child: Stack(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(bottom: 30, right: 10),
                  child: ClipRRect(
                    borderRadius: BorderRadius.circular(5),
                    child: Image.asset(
                      bean.url,
                      width: MediaQuery.of(context).size.width,
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
                Positioned(
                  bottom: 80,
                  left: 15,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Material(
                        color: Colors.transparent,
                        child: Text(
                          bean.location,
                          style: TextStyle(
                            color: Colors.black54,
                            fontSize: 15,
                          ),
                        ),
                      ),
                      Material(
                        color: Colors.transparent,
                        child: Text(
                          bean.name,
                          style: TextStyle(
                            color: Colors.black,
                            fontSize: 20,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
                Positioned(
                  bottom: 0,
                  right: 30,
                  child: Container(
                    width: 60,
                    height: 60,
                    decoration: BoxDecoration(
                      color: Colors.red,
                      borderRadius: BorderRadius.circular(30),
                    ),
                    child: Icon(
                      Icons.arrow_forward,
                      color: Colors.white,
                      size: 30,
                    ),
                  ),
                )
              ],
            ),
          ),
        );
      },
      itemCount: _list.length,
    );
  }
}

//DetailPage.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_ui/TravelBean.dart';

class DetailPage extends StatefulWidget {
  final TravelBean bean;
  DetailPage(this.bean);
  @override
  _DetailPageState createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  final double expanded_height = 400;
  final double rounded_container_height = 50;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          CustomScrollView(
            slivers: <Widget>[
              _buildSliverHead(),
              SliverToBoxAdapter(
                child: _buildDetail(),
              )
            ],
          ),
          Padding(
            padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
            child: SizedBox(
                height: kToolbarHeight,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Padding(
                      padding: EdgeInsets.symmetric(horizontal: 15),
                      child: IconButton(
                        icon: Icon(Icons.arrow_back, color: Colors.white),
                        onPressed: () {
                          Navigator.of(context).pop();
                        },
                      ),
                    ),
                    Padding(
                      padding: EdgeInsets.symmetric(horizontal: 15),
                      child: Icon(Icons.menu, color: Colors.white),
                    )
                  ],
                )),
          )
        ],
      ),
    );
  }

  Widget _buildDetail() {
    return Container(
      color: Colors.white,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          _bulidUserInfo(),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
            child: Text(
              "The balearic Lsnaled,The Lsnaled,The balea balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,The balearic Lsnaled,The balea Lsnaled,"
              "The balearic Lsnaled,The balearic Lsnaled,",
              style: TextStyle(
                  color: Colors.black38,
                  height: 1.4,
                  fontSize: 14,
                  decoration: TextDecoration.none),
            ),
          ),
          Padding(
            padding: EdgeInsets.only(left: 15, right: 30, bottom: 10, top: 10),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text(
                  "Featured",
                  style: TextStyle(
                      color: Colors.black,
                      fontWeight: FontWeight.bold,
                      fontSize: 18,
                      letterSpacing: 1.6,
                      decoration: TextDecoration.none),
                ),
                Text(
                  "View All",
                  style: TextStyle(
                      color: Colors.deepOrange,
                      fontWeight: FontWeight.bold,
                      fontSize: 18,
                      letterSpacing: 1.6,
                      decoration: TextDecoration.none),
                )
              ],
            ),
          ),
          Container(
            padding: EdgeInsets.symmetric(horizontal: 15, vertical: 0),
            child: FeaturedWidget(),
            height: 130,
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
            child: Text(
              "The balearic Lsnaled,The Lsnaled,The balea balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,Lsnaled,The balea The balearic Lsnaled,"
              "The balearic Lsnaled,The balearic Lsnaled,The balea Lsnaled,"
              "The balearic Lsnaled,The balearic Lsnaled,",
              style: TextStyle(
                  color: Colors.black38,
                  height: 1.4,
                  fontSize: 14,
                  decoration: TextDecoration.none),
            ),
          ),
        ],
      ),
    );
  }

  Widget _bulidUserInfo() {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 10, horizontal: 8),
      child: Row(
        children: <Widget>[
          CircleAvatar(
            backgroundColor: Colors.white,
            child: ClipRRect(
              borderRadius: BorderRadius.circular(50),
              child: Image.asset(
                widget.bean.url,
                width: 50,
                height: 50,
                fit: BoxFit.cover,
              ),
            ),
          ),
          SizedBox(width: 10),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text(
                widget.bean.name,
                style: TextStyle(
                    color: Colors.black,
                    fontSize: 18,
                    decoration: TextDecoration.none,
                    fontWeight: FontWeight.bold),
              ),
              Text(
                "Writer,Wonderlust",
                style: TextStyle(
                    color: Colors.black,
                    fontSize: 14,
                    decoration: TextDecoration.none,
                    fontWeight: FontWeight.normal),
              )
            ],
          ),
          Spacer(),
          Icon(
            Icons.share,
            color: Colors.black54,
          )
        ],
      ),
    );
  }

  Widget _buildSliverHead() {
    return SliverPersistentHeader(
        delegate: DetailSliverDelegate(
            expanded_height, widget.bean, rounded_container_height));
  }
}

class FeaturedWidget extends StatelessWidget {
  List<TravelBean> _list = TravelBean.generateMostPopularBean();

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        itemBuilder: (context, index) {
          return Container(
            width: 120,
            height: 130,
            margin: EdgeInsets.only(right: 15),
            child: Image.asset(
              _list[index].url,
              width: 120,
              height: 130,
              fit: BoxFit.cover,
            ),
          );
        },
        itemCount: _list.length,
        scrollDirection: Axis.horizontal);
  }
}

class DetailSliverDelegate extends SliverPersistentHeaderDelegate {
  final double expandedHeight;
  final TravelBean bean;
  final double rounded_container_height;
  DetailSliverDelegate(
      this.expandedHeight, this.bean, this.rounded_container_height);
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarIconBrightness: Brightness.dark,
        statusBarBrightness: Brightness.dark,
      ),
      child: Stack(
        children: <Widget>[
          Hero(
              tag: bean.url,
              child: Image.asset(
                bean.url,
                width: MediaQuery.of(context).size.width,
                fit: BoxFit.cover,
              )),
          Positioned(
            child: Container(
              width: MediaQuery.of(context).size.width,
              height: rounded_container_height,
              decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(30),
                      topRight: Radius.circular(30))),
            ),
            top: expandedHeight - rounded_container_height + 5 - shrinkOffset,
            left: 0,
          ),
          Positioned(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(
                  bean.name,
                  style: TextStyle(
                      color: Colors.white,
                      fontSize: 20,
                      decoration: TextDecoration.none),
                ),
                Text(
                  bean.location,
                  style: TextStyle(
                      color: Colors.white,
                      fontSize: 15,
                      decoration: TextDecoration.none),
                )
              ],
            ),
            top: expandedHeight - 120 - shrinkOffset,
            left: 30,
          ),
        ],
      ),
    );
  }

  @override
  double get maxExtent => expandedHeight;

  @override
  double get minExtent => 0;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }
}

//main.dart

import 'dart:io';

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

void main(){
  runApp(MyApp());
  if(Platform.isAndroid){
      SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(statusBarColor:Colors.white,statusBarBrightness: Brightness.dark,statusBarIconBrightness: Brightness.dark));
  }
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "精美UI",
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

//TravelBean.dart

class TravelBean {
  String name;
  String location;
  String url;

  TravelBean(this.name, this.location, this.url);

  static List<TravelBean> generateTravelBean() {
    return [
      TravelBean("Peach", "Spain ES1", "assets/images/top1.jpg"),
      TravelBean("Grassland", "Spain ES2", "assets/images/top2.jpg"),
      TravelBean("Starry sky", "Spain ES3", "assets/images/top3.jpg"),
      TravelBean("Beauty Pic", "Spain ES4", "assets/images/top4.jpg"),
    ];
  }

  static List<TravelBean> generateMostPopularBean() {
    return [
      TravelBean("Peach", "Spain ES", "assets/images/bottom1.jpg"),
      TravelBean("Grassland", "Spain ES", "assets/images/bottom2.jpg"),
      TravelBean("Starry sky", "Spain ES", "assets/images/bottom3.jpg"),
      TravelBean("Beauty Pic", "Spain ES", "assets/images/bottom4.jpg"),
    ];
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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