内容:
1、列表展示
2、轮播图
3、其他
本次的内容也是在上一节的基础上进行操作
我们就搞这个story模块。
目录:
story.dart story主页面
import 'package:flutter/material.dart'; import 'story_data.dart'; import 'story_item.dart'; void main() => runApp(Story()); class Story extends StatefulWidget { @override _Story createState() => new _Story(); } class _Story extends State<Story> { @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( body: ListView.builder( itemCount: storyData.length, // 构造列表项 itemBuilder: (BuildContext context, int index) { // 传入MessageData返回列表项 return new StoryItem(storyData[index]); }, ), ); } }
story_item.dart 构造列表页面
import 'package:flutter/material.dart'; import 'story_data.dart'; import 'package:date_format/date_format.dart'; import '../common/touch_callback.dart'; import 'story_content.dart'; class StoryItem extends StatelessWidget { final StoryData story; StoryItem(this.story); void main(){ print(this.story); } @override Widget build(BuildContext context) { // TODO: implement build return Container( decoration: BoxDecoration( color: Colors.white, // 底部边 border: Border(bottom: BorderSide(width: 0.5, color: Color(0xFFd9d9d9))), ), height: 64.0, // 按下回调处理空实现 child: TouchCallBack( child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ // 展示头像 Container( // 头像左右留一定外边距 margin: const EdgeInsets.only(left: 13.0, right: 13.0), child: Image.asset(story.image, width: 48.0, height: 48.0,), ), Expanded( // 主标题和子标题采用垂直布局image/message_normal.png child: Column( // 垂直方向居中布局 mainAxisAlignment: MainAxisAlignment.center, // 水平方向靠左对齐 crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( story.title, style: TextStyle(fontSize: 16.0, color:Color(0xFF353535)), maxLines: 1, ), Padding( padding: const EdgeInsets.only(top: 8.0), ), Text( story.subTitle, style: TextStyle(fontSize: 14.0, color:Color(0xFF9a9a9a)), maxLines: 1, // 显示不完的文本用省略号来显示 overflow: TextOverflow.ellipsis, ), ], ), ), ], ), onPressed: () { // 跳转到新的页面 Navigator.push(context, new MaterialPageRoute(builder: (BuildContext context){ return new StoryContent(story); })); }, ), ); } }
里面涉及到两个公共库
touch_callback.dart 触摸回调
import 'package:flutter/material.dart'; // 触摸回调组件 class TouchCallBack extends StatefulWidget{ // 子组件 final Widget child; // 回调函数 final VoidCallback onPressed; final bool isfeed; // 背景色 final Color background; // 传入参数列表 TouchCallBack({Key key, @required this.child, @required this.onPressed, this.isfeed:true, this.background:const Color(0xffd8d8d8), }):super(key: key); @override TouchState createState() => TouchState(); } class TouchState extends State<TouchCallBack> { Color color = Colors.transparent; @override Widget build(BuildContext context) { // TODO: implement build // 返回GestureDetector return GestureDetector( // 使用container容器包裹 child: Container( color: color, child: widget.child, ), // onTap 回调 onTap: widget.onPressed, onPanDown: (d) { if(widget.isfeed == false) return; setState(() { color = widget.background; }); }, onPanCancel: () { setState(() { color = Colors.transparent; }); }, ); } }
story_data.dart 模拟数据
// 聊天数据 class StoryData { // id var id; // 头像 String image; // 主标题 String title; // 副标题 String subTitle; // 描述 String description; // 图集 List imageList; StoryData( this.id, this.image, this.title, this.subTitle , this.description, this.imageList); } List<StoryData> storyData = [ new StoryData( 1, 'images/story/01/cover.jpg', 'episode.1', 'プロローグ', '因为黑沼爽子的外表看起来很阴沉,所以大家给她取了个“贞子”的外号,对想跟大家打成一片的爽子来说,个性开朗活泼又是大家中心人物的风早翔太就成了爽子心中憧憬的对象。在结业式前,爽子心中憧憬的对象风早约了爽子参加试胆大会。', [ 'images/story/01/01.jpg', 'images/story/01/02.jpg', 'images/story/01/03.jpg', 'images/story/01/04.jpg', 'images/story/01/05.jpg', 'images/story/01/06.jpg', ] ), new StoryData( 2, 'images/story/02/cover.jpg', 'episode.2', '席替え', '在大雨中淋的全身湿透的爽子到了学校,看到她这幅模样的同学们都感到无比的害怕,但是却因为这个机会,让爽子和吉田千鹤,矢野有了说话的机会,而爽子也很感谢风早很温柔的对待她。爽子也决定要趁这次换座位的机会下定决心要和坐在隔壁的同学好好相处,可是…。', [ 'images/story/02/01.jpg', 'images/story/02/02.jpg', 'images/story/02/03.jpg', 'images/story/02/04.jpg', 'images/story/02/05.jpg', ] ), new StoryData( 3, 'images/story/03/cover.jpg', 'episode.3', '放课後', '终於可以和自己憧憬的同学自然的说早安。正当爽子正在为这件事感动的时候,这学期代理班导的副班导荒井一市(通称:阿瓶)登场了,阿瓶正想要擅自决定谁来制作出席簿时,不想看到大家困扰的爽子就举起了手…。', [] ), new StoryData( 4, 'images/story/04/cover.jpg', 'episode.4', '噂', '因为千鹤和矢野的关系,爽子终于再次尝到幸福的滋味。可是在学校中突然开始传出中伤千鹤和矢野的流言。虽然千鹤和矢野一开始听到传出谣言的人是爽子的时候是一笑置之不太相信,可是…。', [] ), new StoryData( 5, 'images/story/05/cover.jpg', 'episode.5', '决意', '爽子以为千鹤和矢野的流言是因为自己的关系,而感到自责,所以为了不要再让千鹤和矢野受到伤害,决定回到独来独往的自己,而且也和风早保持距离以免风早也受到牵连。但是千鹤和矢野却被爽子突然冷漠的态度感到迷惘,一直拚命想找出答案。而风早则为了确定爽子的心意决定直接找上爽子…。', [] ), new StoryData( 6, 'images/story/06/cover.jpg', 'episode.6', '友達', '爽子无意间在女生厕所内听到同年级的在谈论千鹤和矢野的谣言,爽子为了要澄清误会,却引起了大騒动,连爽子的同班同学们也都说谣言是爽子传的,但是却遭到风早强烈的否定。就在这个时候千鹤和矢野登场了…。', [] ) ];
story_content.dart 故事详情
import 'package:flutter/material.dart'; import 'story_data.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:carousel_slider/carousel_slider.dart'; //import 'dart:math'; class StoryContent extends StatefulWidget { final StoryData story; StoryContent(this.story); // StoryContent({Key key}) : super(key: key); @override _StoryContent createState() => new _StoryContent(story); } class _StoryContent extends State<StoryContent> { // 获取参数story final StoryData story; _StoryContent(this.story); String number = '124'; String defaultIcon = '0'; void main() { print(1); } void _change() { setState(() { number = defaultIcon == '0'? (int.parse(number) + 1).toString(): (int.parse(number) - 1).toString(); defaultIcon = defaultIcon == '0' ? '1': '0'; }); print(story.imageList); } @override Widget build(BuildContext context) { ScreenUtil.instance = ScreenUtil(width: 750, height: 1334)..init(context); // TODO: implement build return Scaffold( appBar: new AppBar( leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: () { Navigator.pop(context); // 返回 } ), ), body: new Container( alignment: Alignment.center, decoration: BoxDecoration( image: DecorationImage( image: AssetImage('images/bg/flower_one.jpg'), fit: BoxFit.cover, )), child: new Column( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ new Container( alignment: Alignment.center, width: double.infinity, margin: const EdgeInsets.only(top: 30.0, bottom: 10.0), child: Text( story.title + story.subTitle, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20.0, ), ), ), new Container( decoration: new BoxDecoration( border: new Border.all(width: 1.0, color: Colors.pink), color: Colors.grey, borderRadius: new BorderRadius.all(new Radius.circular(4.0)), ), width: ScreenUtil().setWidth(700), child: story.imageList.length != 0 ? new CarouselSlider( items: story.imageList.map((i) { return new Builder( builder: (BuildContext context) { return new Container( width: MediaQuery.of(context).size.width , // margin: new EdgeInsets.only(left: 0, right: 1.0), decoration: new BoxDecoration( color: Colors.amber ), child: new Image.asset( i, fit: BoxFit.fill, ), ); }, ); }).toList(), height: 180.0, autoPlay: false ) : Image.asset(story.image, fit:BoxFit.fill), ), new Container( margin: const EdgeInsets.only(top: 10.0, bottom: 10.0), alignment: Alignment.center, width: ScreenUtil().setWidth(700), child: Text( story.description, style: TextStyle( letterSpacing: 2.0, // wordSpacing: 15.0, ), ), ), // 收藏 new Container( margin: const EdgeInsets.only(top: 4.0, bottom: 10.0), alignment: Alignment.bottomCenter, width: ScreenUtil().setWidth(700), child: new Row( children: <Widget>[ new IconButton( icon: defaultIcon == '0' ? const Icon(Icons.favorite_border, color: Colors.red) : const Icon(Icons.favorite, color: Colors.red), onPressed: _change, ), new Text( number.toString(), ), ], ), ) ], ), ), ); } }
轮播插件
使用方法:
import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; class Page2 extends StatelessWidget { final imagesList = [ 'xxxx.jpg', 'xxxx1.jpg', 'xxxx1.jpg', ] @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("carousel"), ), body: new CarouselSlider( items: imagesList.map((image) { return new Builder( builder: (BuildContext context) { return new Container( width: MediaQuery.of(context).size.width, decoration: new BoxDecoration( color: Colors.amber ), child: new Image.assert( image fit: BoxFit.fill, ) ); }, ); }).toList(), height: 180.0, autoPlay: false //自动播放 ) ); } }
这里有个注意的地方,实时更新数据,渲染页面
setState(() { number = defaultIcon == '0'? (int.parse(number) + 1).toString(): (int.parse(number) - 1).toString(); defaultIcon = defaultIcon == '0' ? '1': '0'; });
截图:
源码地址:https://github.com/ft1107949255/kiminitodoke
今ならできます。