实现基于时间线的 Dismissable ListTile 小部件
Posted
技术标签:
【中文标题】实现基于时间线的 Dismissable ListTile 小部件【英文标题】:Implementing a timeline-based Dismissable ListTile Widget 【发布时间】:2020-09-25 00:25:27 【问题描述】:注意:在进入问题之前,我想让大家进入 问题,这是我的最终目标:
开发一个响应式ListTile
小部件,通过更改其当前状态来响应来自后端的事件。
我将把它留给另一个问题。现在,这是我从这个问题的目标
目标:设计一个 statefull
小部件,它具有多个自定义组件,例如 Circle 和 与时间线相关的小部件
并将其与其他组件一起放在Dismissable Listile
中,如下面的屏幕截图所示
到目前为止,我已经使用 Dismissable ListTile
实现了 ListView
屏幕,其中包含先前设计中显示的 Text
小部件。
这是我目前拥有的代码:
ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index)
final String item = _items[index];
return Dismissible(
direction: DismissDirection.startToEnd,
key: Key(item),
onDismissed: (DismissDirection dir)
setState(() => this._items.removeAt(index));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(dir == DismissDirection.startToEnd
? '$item removed.'
: '$item liked.'),
action: SnackBarAction(
label: 'UNDO',
onPressed: ()
setState(() => this._items.insert(index, item));
,
),
),
);
,
background: Container(
color: Colors.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FaIcon(
FontAwesomeIcons.times,
color: kWhite,
),
SizedBox(
height: 10,
),
Text(
'Cancel Order',
style: TextStyle(color: kWhite),
)
],
),
alignment: Alignment.centerLeft,
),
child: ListTile(
//CustomeTimeLineWidget goes here innstead of the container
title: Container(
child: Column(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Order No: 002345',
),
Text('items no: 12'),
],
),
SizedBox(
height: 20,
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Order Total: 20000',
),
Text('17/04/2020'),
],
),
],
),
),
),
);
,
),
这是从该代码生成的设计
尝试过的解决方案: 我考虑过使用 Timeline Package,但它显示的是垂直时间线而不是水平时间线。
问题:我需要实现时间线(每个ListTile
中的圈子)
【问题讨论】:
如果您想要像素完美的解决方案,请使用CustomPaint
小部件和您自己的 CustomPainter
实现
@pskink 我在问题的最后添加了我的问题的总结,谢谢。
【参考方案1】:
我已根据需要为每个 ListTile 添加了圆圈
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
const kWhite = Colors.white;
class MyHomePage extends StatefulWidget
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
final _items = <Order>[
Order(
id: 'id-1',
date: DateTime.now(),
number: 12,
intemNo: 20,
orderTotal: 20000,
status: OrderStatus.inProgress,
),
Order(
id: 'id-2',
date: DateTime.now().subtract(Duration(days: 10)),
number: 10,
intemNo: 2,
orderTotal: 5000,
status: OrderStatus.delivered,
),
];
DateFormat dateFormate = DateFormat.yMd();
@override
Widget build(BuildContext context)
return ListView.separated(
separatorBuilder: (context, index) => Divider(
indent: 10.0,
endIndent: 10.0,
color: Colors.black,
),
itemCount: _items.length,
itemBuilder: (context, index)
var item = _items[index];
return Dismissible(
direction: DismissDirection.startToEnd,
key: Key(item.id),
onDismissed: (dir)
setState(() => _items.removeAt(index));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(dir == DismissDirection.startToEnd
? '$item removed.'
: '$item liked.'),
action: SnackBarAction(
label: 'UNDO',
onPressed: ()
setState(() => _items.insert(index, item));
,
),
),
);
,
background: Container(
color: Colors.red,
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FaIcon(
FontAwesomeIcons.times,
color: kWhite,
),
SizedBox(
height: 10,
),
Text(
'Cancel \n Order',
style: TextStyle(color: kWhite),
)
],
),
alignment: Alignment.centerLeft,
),
child: ListTile(
title: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Order No: $item.number',
),
Text('items no: $item.intemNo'),
],
),
SizedBox(
height: 20,
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Order Total: $item.orderTotal',
),
Text(dateFormate.format(item.date)),
],
),
SizedBox(
height: 20,
),
StatusBar(
status: item.status,
)
],
),
),
),
);
,
);
class StatusBar extends StatelessWidget
const StatusBar(Key key, this.status) : super(key: key);
final OrderStatus status;
final List<String> titles = const [
'waiting',
'in progress',
'delivering',
'delivered'
];
@override
Widget build(BuildContext context)
var checkedCount = _getCheckedCount(status);
var elements = List<bool>.generate(4, (i) => i < checkedCount);
return Column(
children: <Widget>[
Stack(
children: [
Positioned(
top: 25,
left: 10,
right: 10,
child: Container(
height: 4,
color: Colors.black38,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: elements
.asMap()
.map((index, isChecked) => MapEntry(
index,
Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.deepOrange,
),
alignment: Alignment.center,
width: 50,
height: 50,
child: isChecked
? FaIcon(
FontAwesomeIcons.check,
color: kWhite,
)
: null,
),
Text(titles[index])
],
),
))
.values
.toList(),
),
],
),
],
);
_getCheckedCount(OrderStatus status)
switch (status)
case OrderStatus.waiting:
return 1;
case OrderStatus.inProgress:
return 2;
case OrderStatus.deliviring:
return 3;
case OrderStatus.delivered:
return 4;
class Order
final String id;
final int number;
final int intemNo;
final int orderTotal;
final DateTime date;
final OrderStatus status;
Order(
@required this.id,
@required this.number,
@required this.intemNo,
@required this.orderTotal,
@required this.date,
@required this.status,
);
enum OrderStatus waiting, inProgress, deliviring, delivered
【讨论】:
欣赏它,伙计!非常感谢,我不得不说代码非常清晰!向你致敬,我的朋友。以上是关于实现基于时间线的 Dismissable ListTile 小部件的主要内容,如果未能解决你的问题,请参考以下文章