Listview 实现基于时间轴的可撤销ListTile小部件

Listview 实现基于时间轴的可撤销ListTile小部件,listview,flutter,flutter-layout,Listview,Flutter,Flutter Layout,注意:在开始提问之前,我想让每个人都了解 问题是,我的最终目标是: 开发一个反应式listile小部件,通过更改其当前状态来响应来自后端的事件 我把这个留给另一个问题。现在,这是我的目标 目标:设计一个statefull小部件,该小部件具有多个自定义组件,如圆形和与时间轴相关的小部件 并将其放置在一个可撤销列表文件中和其他组件一起,如下面的屏幕截图所示 到目前为止,我已经用Dismissable listile实现了ListView屏幕,其中包含了先前设计中显示的文本小部件 以下是我目前

注意:在开始提问之前,我想让每个人都了解 问题是,我的最终目标是:

  • 开发一个反应式
    listile
    小部件,通过更改其当前状态来响应来自后端的事件
我把这个留给另一个问题。现在,这是我的目标


目标:设计一个
statefull
小部件,该小部件具有多个自定义组件,如圆形和与时间轴相关的小部件 并将其放置在一个
可撤销列表文件中
和其他组件一起,如下面的屏幕截图所示

到目前为止,我已经用
Dismissable listile
实现了
ListView
屏幕,其中包含了先前设计中显示的
文本
小部件

以下是我目前拥有的代码:

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'),
                  ],
                ),
              ],
            ),
          ),
        ),
      );
    },
  ),
ListView.builder(
itemCount:_items.length,
itemBuilder:(上下文,索引){
最终字符串项=_项[索引];
可驳回的回报(
方向:DismissDirection.startToEnd,
键:键(项),
onDismissed:(DismissDirection目录){
设置状态(()=>this._items.removeAt(index));
Scaffold.of(上下文).showSnackBar(
小吃条(
内容:Text(dir==DismissDirection.startToEnd
?“$项已删除。”
:“$item liked”。),
行动:SnackBarAction(
标签:“撤消”,
已按下:(){
设置状态(()=>this.\u items.insert(index,item));
},
),
),
);
},
背景:集装箱(
颜色:颜色,红色,
子:列(
mainAxisAlignment:mainAxisAlignment.center,
儿童:[
法肯(
FontAwesomeIcons.times,
颜色:kWhite,
),
大小盒子(
身高:10,
),
正文(
“取消订单”,
样式:TextStyle(颜色:kWhite),
)
],
),
对齐:alignment.centerLeft,
),
孩子:ListTile(
//CustomeTimeLineWidget在这里代替容器
标题:集装箱(
子:列(
儿童:[
划船(
mainAxisSize:mainAxisSize.max,
mainAxisAlignment:mainAxisAlignment.spaceBetween,
儿童:[
正文(
‘订单编号:002345’,
),
文本(“项目编号:12”),
],
),
大小盒子(
身高:20,
),
划船(
mainAxisSize:mainAxisSize.max,
mainAxisAlignment:mainAxisAlignment.spaceBetween,
儿童:[
正文(
“订单总数:20000”,
),
文本('17/04/2020'),
],
),
],
),
),
),
);
},
),
这是从代码中得到的设计

尝试过的解决方案: 我考虑过使用时间线包,但它显示的是垂直时间线,而不是水平时间线

问题:我需要实现时间线(每个
列表中的圆圈)

我已经根据您的需要在每个列表中添加了圆圈

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 }
导入“包装:颤振/材料.省道”;
导入“package:font_awesome_flatter/font_awesome_flatter.dart”;
导入“包:intl/intl.dart”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:“颤振演示”,
主题:主题数据(
主样本:颜色。蓝色,
),
家:脚手架(
正文:安全区(
子:MyHomePage(),
),
),
);
}
}
const kWhite=Colors.white;
类MyHomePage扩展StatefulWidget{
@凌驾
_MyHomePageState createState()=>\u MyHomePageState();
}
类_MyHomePageState扩展状态{
最终项目=[
命令(
id:'id-1',
日期:DateTime.now(),
编号:12,
国际编号:20,
订单总数:20000,
状态:OrderStatus.inProgress,
),
命令(
id:'id-2',
日期:DateTime.now().subtract(持续时间(天:10)),
编号:10,
国际:2,
订单总数:5000,
状态:OrderStatus.delivered,
),
];
DateFormat DateFormat=DateFormat.yMd();
@凌驾
小部件构建(构建上下文){
返回ListView.separated(
separatorBuilder:(上下文,索引)=>分隔符(
缩进:10.0,
收尾缩进:10.0,
颜色:颜色,黑色,
),
itemCount:_items.length,
itemBuilder:(上下文,索引){
var项目=_项目[索引];
可驳回的回报(
方向:DismissDirection.startToEnd,
密钥:密钥(项目id),
onDismissed:(dir){
设置状态(()=>_items.removet(索引));
Scaffold.of(上下文).showSnackBar(
小吃条(
内容:Text(dir==DismissDirection.startToEnd
?“$项已删除。”
:“$item liked”。),
行动:SnackBarAction(
标签:“撤消”,
已按下:(){
设置状态(()=>_items.insert(index,item));
},
),
),
);
},
背景:集装箱(
颜色:颜色,红色,
填充:边缘设置。全部(10),
子:列(
mainAxisAlignment:mainAxisAlignment.center,
中国