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),
),
),
),
],
),
);
}
}
flutter 基于时间轴的任务清单
于 2024-09-08 00:22:33 首次发布