Blocks / lists
Task row
Shows a to-do with its checkbox, due day, priority and lateness written in words, read by screen readers as one checkbox node.
Import
Task row is part of Fliin Pro. With a license key, sign in once with fliin login, then add it in a Flutter app where fliin init has run:
fliin login
fliin add task-rowExamples
Todo
A task to do, with its due time and its priority written as a word. Ticking the box calls onDoneChanged; touching the row opens the task with onPressed.
import 'package:your_app/ui/fliin/components/surface/fl_surface.dart';
import 'package:your_app/ui/fliin/components/task_row/fl_task_row.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';
class TaskRowTodoExample extends StatefulWidget {
const TaskRowTodoExample({super.key});
@override
State<TaskRowTodoExample> createState() => _TaskRowTodoExampleState();
}
class _TaskRowTodoExampleState extends State<TaskRowTodoExample> {
var _done = false;
@override
Widget build(BuildContext context) {
return FlSurface(
padding: EdgeInsetsDirectional.symmetric(horizontal: FlScope.of(context).space.lg),
child: FlTaskRow(
title: 'Send Leah the quote',
done: _done,
onDoneChanged: (done) => setState(() => _done = done),
due: 'Today · 4:30 PM',
priority: FlTaskPriority.medium,
priorityLabel: 'Medium priority',
onPressed: () {},
openLabel: 'Edit',
),
);
}
}Overdue
A task past its due time: overdueLabel names the delay for the screen reader and the due time turns to danger.
import 'package:your_app/ui/fliin/components/surface/fl_surface.dart';
import 'package:your_app/ui/fliin/components/task_row/fl_task_row.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';
class TaskRowOverdueExample extends StatefulWidget {
const TaskRowOverdueExample({super.key});
@override
State<TaskRowOverdueExample> createState() => _TaskRowOverdueExampleState();
}
class _TaskRowOverdueExampleState extends State<TaskRowOverdueExample> {
var _done = false;
@override
Widget build(BuildContext context) {
return FlSurface(
padding: EdgeInsetsDirectional.symmetric(horizontal: FlScope.of(context).space.lg),
child: FlTaskRow(
title: 'Pay the rent',
done: _done,
onDoneChanged: (done) => setState(() => _done = done),
due: 'Yesterday · 6:00 PM',
overdueLabel: 'Overdue',
priority: FlTaskPriority.high,
priorityLabel: 'High priority',
onPressed: () {},
openLabel: 'Edit',
),
);
}
}Done
A finished task: its title is struck through and quieter. Untick it to bring it back.
import 'package:your_app/ui/fliin/components/surface/fl_surface.dart';
import 'package:your_app/ui/fliin/components/task_row/fl_task_row.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';
class TaskRowDoneExample extends StatefulWidget {
const TaskRowDoneExample({super.key});
@override
State<TaskRowDoneExample> createState() => _TaskRowDoneExampleState();
}
class _TaskRowDoneExampleState extends State<TaskRowDoneExample> {
var _done = true;
@override
Widget build(BuildContext context) {
return FlSurface(
padding: EdgeInsetsDirectional.symmetric(horizontal: FlScope.of(context).space.lg),
child: FlTaskRow(
title: 'Buy light bulbs',
done: _done,
onDoneChanged: (done) => setState(() => _done = done),
onPressed: () {},
openLabel: 'Edit',
),
);
}
}Adding
A task on its way to the server: status says so and onDoneChanged: null keeps the box until it is saved.
import 'package:your_app/ui/fliin/components/surface/fl_surface.dart';
import 'package:your_app/ui/fliin/components/task_row/fl_task_row.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';
class TaskRowAddingExample extends StatelessWidget {
const TaskRowAddingExample({super.key});
@override
Widget build(BuildContext context) {
return FlSurface(
padding: EdgeInsetsDirectional.symmetric(horizontal: FlScope.of(context).space.lg),
child: const FlTaskRow(title: 'Buy bread', done: false, onDoneChanged: null, status: 'Adding…'),
);
}
}Not added
A task the server refused: statusFailed marks the status as an error, and the footer offers to try again or to remove it.
import 'package:your_app/ui/fliin/components/link_button/fl_link_button.dart';
import 'package:your_app/ui/fliin/components/surface/fl_surface.dart';
import 'package:your_app/ui/fliin/components/task_row/fl_task_row.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';
class TaskRowNotAddedExample extends StatelessWidget {
const TaskRowNotAddedExample({super.key});
@override
Widget build(BuildContext context) {
final space = FlScope.of(context).space;
return FlSurface(
padding: EdgeInsetsDirectional.symmetric(horizontal: space.lg),
child: FlTaskRow(
title: 'Call the bank',
done: false,
onDoneChanged: null,
status: 'Not added',
statusFailed: true,
footer: Wrap(
spacing: space.lg,
children: [
FlLinkButton(label: 'Try again', onPressed: () {}),
FlLinkButton(label: 'Remove', onPressed: () {}),
],
),
),
);
}
}Tagged
Tasks as an issue tracker shows them: their labels in colored chips (tags), and the assignee's avatar at the end (trailing), named for the screen reader by trailingLabel.
import 'package:your_app/ui/fliin/components/avatar/fl_avatar.dart';
import 'package:your_app/ui/fliin/components/chip/fl_chip.dart';
import 'package:your_app/ui/fliin/components/surface/fl_surface.dart';
import 'package:your_app/ui/fliin/components/task_row/fl_task_row.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';
class TaskRowTaggedExample extends StatefulWidget {
const TaskRowTaggedExample({super.key});
@override
State<TaskRowTaggedExample> createState() => _TaskRowTaggedExampleState();
}
class _TaskRowTaggedExampleState extends State<TaskRowTaggedExample> {
final _done = <int>{};
@override
Widget build(BuildContext context) {
FlTaskRow task(int id, String title, List<FlTaskTag> tags, int photo, String assignee, {String? due}) =>
FlTaskRow(
title: title,
done: _done.contains(id),
onDoneChanged: (done) => setState(() => done ? _done.add(id) : _done.remove(id)),
due: due,
tags: tags,
trailing: FlAvatar(image: AssetImage('images/people/$photo.jpg'), size: FlAvatarSize.sm),
trailingLabel: 'Assigned to $assignee',
onPressed: () {},
openLabel: 'Edit',
);
return FlSurface(
padding: EdgeInsetsDirectional.symmetric(horizontal: FlScope.of(context).space.lg),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
task(1, 'Fix the checkout total rounding', const [
FlTaskTag('Bug', color: FlChipColor.danger),
FlTaskTag('Payments', color: FlChipColor.success),
], 1, 'Kate Wilson', due: 'Today'),
task(2, 'Draft the onboarding screens', const [
FlTaskTag('Design', color: FlChipColor.accent),
], 2, 'John Doe', due: 'Fri, Nov 7'),
task(3, 'Update the privacy policy', const [FlTaskTag('Legal', color: FlChipColor.warning)], 4, 'Michael Brown'),
],
),
);
}
}Usage
The code the reference images render:
final example = ColoredBox(
color: FlScope.of(context).colors.surface,
child: SingleChildScrollView(
padding: EdgeInsetsDirectional.all(FlScope.of(context).space.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FlTaskRow(
title: (variant.dir == TextDirection.rtl
? "إرسال عرض السعر إلى ليا"
: "Send Leah the quote"),
done: false,
onDoneChanged: (_) {},
due: (variant.dir == TextDirection.rtl
? "اليوم · \u2066١٦:٣٠\u2069"
: "Today · 16:30"),
priority: FlTaskPriority.medium,
priorityLabel: (variant.dir == TextDirection.rtl
? "أولوية متوسطة"
: "Medium priority"),
onPressed: () {},
openLabel: (variant.dir == TextDirection.rtl ? "تعديل" : "Edit"),
),
FlTaskRow(
title: (variant.dir == TextDirection.rtl
? "دفع الإيجار"
: "Pay the rent"),
done: false,
onDoneChanged: (_) {},
due: (variant.dir == TextDirection.rtl
? "أمس · \u2066١٨:٠٠\u2069"
: "Yesterday · 18:00"),
overdueLabel: (variant.dir == TextDirection.rtl
? "متأخرة"
: "Overdue"),
priority: FlTaskPriority.high,
priorityLabel: (variant.dir == TextDirection.rtl
? "أولوية عالية"
: "High priority"),
onPressed: () {},
openLabel: (variant.dir == TextDirection.rtl ? "تعديل" : "Edit"),
),
FlTaskRow(
title: (variant.dir == TextDirection.rtl
? "شراء مصابيح"
: "Buy light bulbs"),
done: true,
onDoneChanged: (_) {},
onPressed: () {},
openLabel: (variant.dir == TextDirection.rtl ? "تعديل" : "Edit"),
),
FlTaskRow(
title: (variant.dir == TextDirection.rtl
? "شراء الخبز"
: "Buy bread"),
done: false,
onDoneChanged: null,
status: (variant.dir == TextDirection.rtl
? "جارٍ الإضافة…"
: "Adding…"),
),
FlTaskRow(
title: (variant.dir == TextDirection.rtl
? "الاتصال بالبنك"
: "Call the bank"),
done: false,
onDoneChanged: null,
status: (variant.dir == TextDirection.rtl ? "لم تُضف" : "Not added"),
statusFailed: true,
footer: Wrap(
spacing: FlScope.of(context).space.lg,
children: [
FlLinkButton(
label: (variant.dir == TextDirection.rtl
? "أعد المحاولة"
: "Try again"),
onPressed: () {},
),
FlLinkButton(
label: (variant.dir == TextDirection.rtl ? "إزالة" : "Remove"),
onPressed: () {},
),
],
),
),
],
),
),
);API
FlTaskPriority: enum {low, medium, high}
FlTaskPriority.low: static final FlTaskPriority
FlTaskPriority.medium: static final FlTaskPriority
FlTaskPriority.high: static final FlTaskPriority
FlTaskRowAction: final class
FlTaskRowAction.new: const new({required String label, required VoidCallback onPressed})
FlTaskRowAction.label: get String
FlTaskRowAction.onPressed: get VoidCallback
FlTaskTag: final class
FlTaskTag.new: const new(String label, {FlChipColor color})
FlTaskTag.label: get String
FlTaskTag.color: get FlChipColor
FlTaskRow: class extends StatelessWidget
FlTaskRow.new: const new({super.key, required String title, required bool done, required ValueChanged<bool>? onDoneChanged, String? due, String? overdueLabel, bool overdueShown, FlTaskPriority? priority, String? priorityLabel, String? status, bool statusFailed, VoidCallback? onPressed, String? openLabel, List<FlTaskRowAction> actions, Widget? footer, String separator, List<FlTaskTag> tags, Widget? trailing, String? trailingLabel})
FlTaskRow.title: get String
FlTaskRow.done: get bool
FlTaskRow.onDoneChanged: get ValueChanged<bool>?
FlTaskRow.due: get String?
FlTaskRow.overdueLabel: get String?
FlTaskRow.overdueShown: get bool
FlTaskRow.priority: get FlTaskPriority?
FlTaskRow.priorityLabel: get String?
FlTaskRow.status: get String?
FlTaskRow.statusFailed: get bool
FlTaskRow.onPressed: get VoidCallback?
FlTaskRow.openLabel: get String?
FlTaskRow.actions: get List<FlTaskRowAction>
FlTaskRow.footer: get Widget?
FlTaskRow.separator: get String
FlTaskRow.tags: get List<FlTaskTag>
FlTaskRow.trailing: get Widget?
FlTaskRow.trailingLabel: get String?
FlTaskRow.build: Widget(BuildContext context)Reference renders
The 16 images published with version 0.2.5, rendered by the shared harness on macOS with Flutter 3.47.7. fliin verify task-row compares your copy to them.
















Use it to
- toggle a task between done and to do
- show when a task is due and that it is late
- show how urgent a task is
- open a task to edit it
- show a task the server has not received yet
- Not for
- a single box in a form: use the checkbox (checkbox)
- an agenda event: use an event row (event-row)
- States