flutter 基于时间轴的任务清单

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
      debugShowCheckedModeBanner: false,
      debugShowMaterialGrid: false,
      // locale: Locale('zh'),
      // // localizationsDelegates: const [
      // //   GlobalMaterialLocalizations.delegate,
      // // ],
      // supportedLocales: const [
      //   //此处
      //   Locale('zh', 'CH'),
      //   Locale('en', 'US'),
      // ],
      home: Scaffold(appBar: AppBar(title: const Text('时间轴')), body: const App())));
}

class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  List<Widget> tasks = [];
  Size size = const Size(500, 500);

  @override
  void initState() {


    // TODO: implement initState
    super.initState();
    List<String> taskStatus = ["未完成","进行中", "已完成"];
    tasks = List.generate(6, (i) {
      return buildTask(TaskData(
          title: "8-25 电商订单任务 $i",
          startHour: 8 + i,
          startMinute: 40+i*4,
          endHour: 8 + i + 1,
          endMinute: 20+i*2,
          context: "增加订单,正价订单明细,增加订单,正价订单明细增加订单,正价订单明细增加订单,正价订单明细",
          status: taskStatus[i % 3]));
    });
    // size = MediaQuery.of(context).size;
  }

  @override
  Widget build(BuildContext context) {
    size = MediaQuery.of(context).size;
    return Center(
        child: SingleChildScrollView(
            scrollDirection: Axis.vertical,
            child: Container(
                width: size.width,
                height: 1800,
                child: Stack(
                  children: [Timeline(), ...tasks],
                ))));
  }

  Widget buildTask(TaskData data) {
    double top = TimelinePainter.getPositionY(data.startHour, data.startMinute);
    double height =
        TimelinePainter.getPositionY(data.endHour, data.endMinute) - TimelinePainter.getPositionY(data.startHour, data.startMinute);
    // height=height<60?60:height;
    return Positioned(
        left: 80,
        top: top,
        width: 350,
        height: height,
        child: TaskBox(
          data: data,
          width: 350,
          height: height,
        ));
  }
}

class Timeline extends StatelessWidget {
  const Timeline({super.key});

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(MediaQuery.of(context).size.width, 500),
      painter: TimelinePainter(),
    );
  }
}

class TimelinePainter extends CustomPainter {
  static double start = 10;
  static double gap = 30;
  static double getPositionY(int hour, int mins) {
    int i = hour - 8; // 从早上8点开始
    double yPosition = 10 + i.toDouble() * gap * 6 + mins / 10 * gap;
    return yPosition;
  }

  @override
  void paint(Canvas canvas, Size size) {
    const textStyle = TextStyle(fontSize: 12,fontWeight: FontWeight.bold, color: Colors.green);
    for (int i = 0; i < 9; i++) {
      final hour = i + 8; // 从早上8点开始
      final yPosition = 10 + i.toDouble() * gap * 6; // 每个小时分成4格,每格10个像素高度
      // 绘制整点的时间标签
      var textSpan = TextSpan(text: '$hour:00', style: textStyle);
      var textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr);
      textPainter.layout();

      textPainter.paint(canvas, Offset(40, yPosition - textPainter.height / 2));
      for (int j = 0; j < 6; j++) {
        if (j == 0) {
          canvas.drawLine(Offset(5, yPosition), Offset(25, yPosition), Paint()..color = Colors.red);
        } else {
          canvas.drawLine(Offset(5, yPosition + j * gap), Offset(15, yPosition + j * gap), Paint()..color = Colors.black);
        }
      }
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}


class TaskData{
  String title;
  int startHour;
  int startMinute;
  int endHour;
  int endMinute;
  String context;
  String status;
  TaskData({this.title='',this.startHour=0,this.startMinute=0,this.endHour=0,this.endMinute=0,this.context='',this.status='未完成'});
}
class TaskBox extends StatelessWidget {
  TaskData data;
  double width = 400;
  double height = 100;
  TaskBox({super.key, required this.data,this.width=400,this.height=60});
  //"未完成","进行中", "已完成"
  Map<String,Color> statusColorMap = {
    "未完成": Colors.pink,
    "进行中": Colors.lightBlue,
    "已完成": Colors.green,
  };
  @override
  Widget build(BuildContext context) {
    return Container(
      width: width,
      height: height,
      padding: const EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: statusColorMap[data.status],
        borderRadius: BorderRadius.circular(8.0),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.1),
            blurRadius: 4.0,
            spreadRadius: 2.0,
          ),
        ],
      ),
      child: Row(
        // direction: Axis.horizontal,
        children: [
          Expanded(
            flex: 85,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Center(child: Text(
                  data.title,
                  style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold,color: Colors.white),
                ),),
                const SizedBox(height: 8),
                Text(
                  '时间:${data.startHour}:${data.startMinute} ~ ${data.endHour}:${data.endMinute}',
                  style: const TextStyle(fontSize: 12, color: Colors.white),
                ),
                const SizedBox(height: 8),
                Expanded(child: Text(
                  data.context,
                  style: const TextStyle(fontSize: 12, color: Colors.white),
                )),
              ],
            ),
          ),
          Expanded(
            flex: 15,
            child: RotatedBox(
              quarterTurns: 1,
              child: Text(
                data.status,
                style: const TextStyle(fontSize: 14, color: Colors.white),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值