(本视频为原创视频)

Flutter如何实现如下精美UI:

f462f4a22ea91de7ea97e4e20cff359b.png

视频如下:

效果及源码地址:

web端访问:http://ijitang.net/

图片地址:https://dribbble.com/shots/6574276--8-Travel-Guides-Experience-Freebie

源码地址:https://github.com/jiang111/flutter_code

实现思路:

主要难点在于列表页跳转到详情页的时候图片的hero效果,以及第二个页面中的上滑效果样式的问题,一般上滑效果都会采用sliver来实现,原本采用的是

NestedScrollView + headerSliverBuilder 来实现,

但是在实现的时候发现无法做出中间以图片作为背景的白色圆角,于是才用了CustomScrollView+Slivers实现,Sliver用SliverPersistentHeader自定义DetailSliverDelegate实现,body部分采用SliverToBoxAdapter实现,部分代码如下:

hero部分:

       Hero(       //以url作为tag,保证不会重复            tag: bean.url,            child: Stack(              children: [                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: [                      Material(                        color: Colors.transparent,                        child: Text(                          bean.location,                          style: TextStyle(                            color: Colors.white,                            fontSize: 15,                          ),                        ),                      ),                      //...省略不重要代码                    ],                  ),                ),           ),

主要的slivers部分:

CustomScrollView(            slivers: [              _buildSliverHead(),              SliverToBoxAdapter(                child: _buildDetail(),              )            ],          ),                     //_buildSliverHead 方法  Widget _buildSliverHead() {    return SliverPersistentHeader(      delegate: DetailSliverDelegate(        expanded_height,        widget.bean,        rounded_container_height,      ),    );  }//_buildDetail方法 

DetailSliverDelegate类:

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) {   //此处是为了实现Android中的沉浸式状态栏,以及ios的状态栏    return AnnotatedRegion(      value: SystemUiOverlayStyle(        statusBarColor: Colors.transparent,        //使用dark模式可以让状态栏的字变为黑色,而图片是白色,这样才能看到状态栏的文字        statusBarIconBrightness: Brightness.dark,        statusBarBrightness: Brightness.dark,      ),      child: Stack(        children: [          Hero(            tag: bean.url,            child: Image.asset(              bean.url,              width: MediaQuery.of(context).size.width,              height: expandedHeight,              fit: BoxFit.cover,            ),          ),          Positioned(            top: expandedHeight - rounded_container_height - shrinkOffset,            left: 0,            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),                ),              ),            ),          ),          Positioned(            top: expandedHeight - 120 - shrinkOffset,            left: 30,            child: Column(              crossAxisAlignment: CrossAxisAlignment.start,              children: [                Text(                  bean.name,                  style: TextStyle(                    color: Colors.white,                    fontSize: 30,                  ),                ),                Text(                  bean.location,                  style: TextStyle(                    color: Colors.white,                    fontSize: 15,                  ),                ),              ],            ),          )        ],      ),    );  }   @override  double get maxExtent => expandedHeight;  @override  double get minExtent => 0; // 此处为0代表可以一直划上去,不出现pinned效果  @override  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {    return true;  }}

说明:

图片来自dribbble,如果图片侵权请联系作者删除

为什么文章中会出现广告? 写代码的也要赚点零花钱吃饭的嘛

点击原文链接可跳转至bilibili观看

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐