قائمة المهام: الحالات الفارغة
جزء من قسم المشاريع في رحلة Flutter على Coddy. الدرس 9 من 36.
اضغط على «المنجَز» قبل أن تنجز أي شيء، وستظهر لك شاشة فارغة. هل التطبيق معطّل، أم لا يوجد شيء فيها؟ توضّح الحالة الفارغة ذلك: أيقونة هادئة وسطر يشرح ما يحدث.
لكل عامل تصفية رسالته الخاصة، لذا يختار تعبير switch على _filter الرسالة المناسبة:
String get _emptyMessage => switch (_filter) {
Filter.all => 'No tasks yet',
Filter.active => 'Nothing left to do',
Filter.done => 'Nothing done yet',
};ثم يعرض موضع القائمة أحد الأمرين: shown.isEmpty ? Center(...) : ListView.builder(...). اختبر القائمة المعروضة، لا _tasks: فعند اختيار «المنجَز»، لا تكون القائمة الكاملة فارغة، لكن الشاشة تكون كذلك.
امنح الأيقونة لون outline الخاص بالنسق، Theme.of(context).colorScheme.outline. إنه الرمادي الهادئ للون الأساسي الذي اخترته، لذا تبدو الحالة الفارغة جزءًا من التطبيق.
هذا هو الدرس الأخير في قائمة المهام. تختبر الفحوصات التطبيق بأكمله: الإضافة، ووضع علامة، والسحب، والتصفية.
التحدي
متوسطاعرض حالة فارغة عندما تكون shown فارغة:
- أضف أداة getter باسم
_emptyMessage:No tasks yetللكل، وNothing left to doللنشطة، وNothing done yetللمنجزة - اعرض مكان القائمة عنصر
Columnفي المنتصف، يتضمن أيقونةIcons.task_alt(بحجم 64، وبلونoutlineالخاص بالنسق)، ومسافة قدرها 12 بكسل، والرسالة - وإلا، فاعرض القائمة كما كانت من قبل
جرّب بنفسك
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
),
home: const TodoScreen(),
);
}
}
class Task {
Task(this.title);
final String title;
bool done = false;
}
enum Filter { all, active, done }
class TodoScreen extends StatefulWidget {
const TodoScreen({super.key});
@override
State<TodoScreen> createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoScreen> {
final List<Task> _tasks = [Task('Buy milk'), Task('Walk the dog'), Task('Read a book')];
final _controller = TextEditingController();
Filter _filter = Filter.all;
int get _left => _tasks.where((task) => !task.done).length;
List<Task> get _shown => switch (_filter) {
Filter.all => _tasks,
Filter.active => _tasks.where((task) => !task.done).toList(),
Filter.done => _tasks.where((task) => task.done).toList(),
};
void _addTask() {
final title = _controller.text.trim();
if (title.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Type a task first')),
);
return;
}
setState(() {
_tasks.add(Task(title));
});
_controller.clear();
}
void _deleteTask(Task task) {
final index = _tasks.indexOf(task);
setState(() {
_tasks.remove(task);
});
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Deleted ${task.title}'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
setState(() {
_tasks.insert(index, task);
});
},
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final shown = _shown;
return Scaffold(
appBar: AppBar(title: const Text('My Tasks')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
labelText: 'New task',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 8),
IconButton.filled(
onPressed: _addTask,
icon: const Icon(Icons.add),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Text(
_left == 1 ? '1 task left' : '$_left tasks left',
style: Theme.of(context).textTheme.titleSmall,
),
],
),
),
SegmentedButton<Filter>(
segments: const [
ButtonSegment(value: Filter.all, label: Text('All')),
ButtonSegment(value: Filter.active, label: Text('Active')),
ButtonSegment(value: Filter.done, label: Text('Done')),
],
selected: {_filter},
onSelectionChanged: (selection) {
setState(() {
_filter = selection.first;
});
},
),
const SizedBox(height: 8),
Expanded(
child: ListView.builder(
itemCount: shown.length,
itemBuilder: (context, index) {
final task = shown[index];
return Dismissible(
key: ObjectKey(task),
direction: DismissDirection.endToStart,
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.symmetric(horizontal: 24),
child: const Icon(Icons.delete, color: Colors.white),
),
onDismissed: (direction) => _deleteTask(task),
child: ListTile(
leading: Checkbox(
value: task.done,
onChanged: (value) {
setState(() {
task.done = value!;
});
},
),
title: Text(
task.title,
style: task.done
? const TextStyle(
decoration: TextDecoration.lineThrough,
color: Colors.grey,
)
: null,
),
),
);
},
),
),
],
),
);
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس المشاريع
تدرّب بنفسك: مترجم Flutter عبر الإنترنت