Flutter自定義日曆【Flutter 專題 11】

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們使用的日曆多年來一直在發展。從手寫日曆到印刷日曆,我們現在手上都有一個數字日曆,它是高度可定製的,並在我們需要提醒的準確時刻提醒我們我們的事件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們將看到如何在 Flutter 中構建和自定義日曆小部件,以便爲我們的用戶提供這種體驗。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"儘管 Flutter 以日期和時間選擇器的形式提供了一個日曆小部件,它提供了可自定義的顏色、字體和用法,但它缺少一些功能。您可以使用它來選擇日期和時間(或兩者)並將其添加到您的應用程序中,但它需要與一個按鈕和一個佔位符相結合,可以保存選擇的日期或時間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,我將從 Flutter 架構提供的原生日曆開始,然後轉到pub.dev 上最流行的日曆小部件","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"。您還可以使用許多其他流行的日曆小部件,但在本教程中,我們將深入介紹。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Flutter 日曆小部件","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TableCalendar ()","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Flutter 日曆小部件(日期選擇器和時間選擇器)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了更徹底地解釋這個小部件,","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,讓我們回顧一下","attrs":{}},{"type":"link","attrs":{"href":"https://api.flutter.dev/flutter/material/showDatePicker.html","title":"","type":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"showDatePicker","attrs":{}}],"attrs":{}},{"type":"text","text":"默認構造函數","attrs":{}}]},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"showDatePicker({\n// it requires a context\n required BuildContext context, \n// when datePicker is displayed, it will show month of the current date\n required DateTime initialDate, \n// earliest possible date to be displayed (eg: 2000)\n required DateTime firstDate,\n// latest allowed date to be displayed (eg: 2050)\n required DateTime lastDate,\n// it represents TODAY and it will be highlighted\n DateTime? currentDate,\n // either by input or selected, defaults to calendar mode.\n DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar or input,\n// restricts user to select date from range to dates.\n SelectableDayPredicate? selectableDayPredicate,\n// text that is displayed at the top of the datePicker\n String? helpText,\n// text that is displayed on cancel button\n String? cancelText,\n// text that is displayed on confirm button\n String? confirmText,\n// use builder function to customise the datePicker \n TransitionBuilder? Builder,\n// option to display datePicker in year or day mode. Defaults to day\n DatePickerMode initialDatePickerMode = DatePickerMode.day or year,\n// error message displayed when user hasn't entered date in proper format\n String? errorFormatText,\n// error message displayed when date is not selectable\n String? errorInvalidText,\n// hint message displayed to prompt user to enter date according to the format mentioned (eg: dd/mm/yyyy)\n String? fieldHintText,\n// label message displayed for what the user is entering date for (eg: birthdate)\n String? fieldLabelText,\n})\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於上面的默認構造函數,大家可以參考下圖,我已經指出了一些重要的屬性,可以根據自己的需要進行自定義。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/2837edd93c38331dfdbbb6de797ab268.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"它是如何工作的?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我會先給答案加整個代碼,以作展示,用戶可以輸入會議名稱和鏈接,然後選擇日期和時間。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"整個代碼","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/781ffdcd4ae2a1c9fca4ea54fcf1477d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"import 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\n\n\nconst Color darkBlue = Color.fromARGB(255, 18, 32, 47);\n\nvoid main() {\n runApp(MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return MaterialApp(\n theme: ThemeData.dark().copyWith(\n scaffoldBackgroundColor: darkBlue,\n ),\n debugShowCheckedModeBanner: false,\n home: FlutterDatePickerExample());\n }\n}\n\nclass FlutterDatePickerExample extends StatelessWidget {\n final ValueNotifier dateSub = ValueNotifier(null);\n final ValueNotifier longDateSub = ValueNotifier(null);\n final ValueNotifier timeSub = ValueNotifier(null);\n final ValueNotifier timeSubShort = ValueNotifier(null);\n final TextEditingController meetingName = TextEditingController();\n final TextEditingController meetingLink = TextEditingController();\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: const Text('Vanilla Calendar Flutter'),\n ),\n body: Padding(\n padding: const EdgeInsets.all(14.0),\n child: SingleChildScrollView(\n child:Column(\n crossAxisAlignment: CrossAxisAlignment.stretch,\n children: [\n const Text(\n ' 創建會議',\n textAlign: TextAlign.center,\n style: TextStyle(fontSize: 24.0),\n ),\n const SizedBox(\n height: 20,\n ),\n buildTextField(controller: meetingName, hint: '輸入會議名稱'),\n const SizedBox(\n height: 20,\n ),\n buildTextField(controller: meetingLink, hint: '輸入會議鏈接'),\n const SizedBox(\n height: 10,\n ),\n const Text(\n ' Short Date',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: dateSub,\n builder: (context, dateVal, child) {\n return InkWell(\n onTap: () async {\n DateTime? date = await showDatePicker(\n context: context,\n initialDate: DateTime.now(),\n firstDate: DateTime.now(),\n lastDate: DateTime(2050),\n currentDate: DateTime.now(),\n initialEntryMode: DatePickerEntryMode.calendar,\n initialDatePickerMode: DatePickerMode.day,\n builder: (context, child) {\n return Theme(\n data: Theme.of(context).copyWith(\n colorScheme: const ColorScheme.light(\n primary: Colors.blueGrey,\n onSurface: AppColors.blackCoffee,\n )\n ),\n child: child!,\n );\n });\n dateSub.value = date;\n },\n child: buildDateTimePicker(\n dateVal != null ? convertDate(dateVal) : ''));\n }),\n const SizedBox(\n height: 10,\n ),\n const Text(\n ' 12H Format Time',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: timeSubShort,\n builder: (context, timeVal, child) {\n return InkWell(\n onTap: () async {\n TimeOfDay? time = await showTimePicker(\n context: context,\n builder: (context, child) {\n return Theme(\n data: Theme.of(context)\n child: child!,\n );\n },\n initialTime: TimeOfDay.now(),\n );\n timeSubShort.value = time;\n },\n child: buildDateTimePicker(timeVal != null\n ? convertTime(timeVal)\n : ''));\n }),\n const SizedBox(\n height: 20.0,\n ),\n const Text(\n ' Long Date',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: longDateSub,\n builder: (context, dateVal, child) {\n return InkWell(\n onTap: () async {\n DateTime? date = await showDatePicker(\n context: context,\n initialDate: DateTime.now(),\n firstDate: DateTime.now(),\n lastDate: DateTime(2050),\n builder: (context, child) {\n return Theme(\n data: Theme.of(context),\n child: child!,\n );\n });\n longDateSub.value = date;\n },\n child: buildDateTimePicker(\n dateVal != null ? longDate(dateVal) : ''));\n }),\n const SizedBox(\n height: 10,\n ),\n const Text(\n ' 24H Format Time',\n textAlign: TextAlign.left,\n style: TextStyle(fontSize: 18.0),\n ),\n ValueListenableBuilder(\n valueListenable: timeSub,\n builder: (context, timeVal, child) {\n return InkWell(\n onTap: () async {\n TimeOfDay? time = await showTimePicker(\n context: context,\n builder: (context, child) {\n return MediaQuery(\n data: MediaQuery.of(context).copyWith(\n alwaysUse24HourFormat: true),\n child: child!,\n );\n },\n initialTime: TimeOfDay.now(),\n );\n timeSub.value = time;\n },\n child: buildDateTimePicker(timeVal != null\n ? timeVal.format(context)\n : ''));\n }),\n const SizedBox(height: 20.0,),\n ElevatedButton(onPressed: () {\n ScaffoldMessenger.of(context).showSnackBar(const SnackBar(\n content: Text('會議創建成功'),\n duration: Duration(seconds: 5),));\n }, child: const Text('提交')),\n ],\n ),\n ),\n \n ),\n );\n }\n\n String convertDate(DateTime dateTime) {\n return DateFormat('yyyy年MM月dd日').format(dateTime);\n }\n\n String longDate(DateTime dateTime) {\n return DateFormat('EEE, MMM d, yyy').format(dateTime);\n }\n\n String convertTime(TimeOfDay timeOfDay) {\n DateTime tempDate = DateFormat('hh:mm').parse(\n timeOfDay.hour.toString() + ':' + timeOfDay.minute.toString());\n var dateFormat = DateFormat('h:mm a');\n return dateFormat.format(tempDate);\n }\n\n\n Widget buildDateTimePicker(String data) {\n return ListTile(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.circular(10.0),\n side: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n ),\n title: Text(data),\n trailing: const Icon(\n Icons.calendar_today,\n color: AppColors.eggPlant,\n ),\n );\n }\n\n Widget buildTextField(\n {String? hint, required TextEditingController controller}) {\n return TextField(\n controller: controller,\n textCapitalization: TextCapitalization.words,\n decoration: InputDecoration(\n labelText: hint ?? '',\n focusedBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n enabledBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n ),\n );\n }\n \n}\n class AppColors {\n AppColors._();\n\n static const Color blackCoffee = Color(0xFF352d39);\n static const Color eggPlant = Color(0xFF6d435a);\n static const Color celeste = Color(0xFFb1ede8);\n static const Color babyPowder = Color(0xFFFFFcF9);\n static const Color ultraRed = Color(0xFFFF6978);\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"第 1 步:實施 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ValueNotifier","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我已經實現了一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ValueNotifier","attrs":{}}],"attrs":{}},{"type":"text","text":"將在文本字段中保存日期的方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"final ValueNotifier dateSub = ValueNotifier(null);\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"第 2 步:創建","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"datePicker","attrs":{}}],"attrs":{}},{"type":"text","text":"對話框","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隨着","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ValueListenerBuilder","attrs":{}}],"attrs":{}},{"type":"text","text":"和實例","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DateTime","attrs":{}}],"attrs":{}},{"type":"text","text":",並與InkWell","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"小部件,當我們點擊","attrs":{}}],"attrs":{}},{"type":"text","text":"textField","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":",一個","attrs":{}}],"attrs":{}},{"type":"text","text":"datePicker","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對話框會彈出。當用戶點擊所需的日期時,它將顯示在","attrs":{}}],"attrs":{}},{"type":"text","text":"textField`:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"ValueListenableBuilder(\n valueListenable: dateSub,\n builder: (context, dateVal, child) {\n return InkWell(\n onTap: () async {\n DateTime? date = await showDatePicker(\n context: context,\n initialDate: DateTime.now(),\n firstDate: DateTime.now(),\n lastDate: DateTime(2050),\n currentDate: DateTime.now(),\n initialEntryMode: DatePickerEntryMode.calendar,\n initialDatePickerMode: DatePickerMode.day,\n builder: (context, child) {\n return Theme(\n data: Theme.of(context).copyWith(\n colorScheme: ColorScheme.fromSwatch(\n primarySwatch: Colors.blueGrey,\n accentColor: AppColors.blackCoffee,\n backgroundColor: Colors.lightBlue,\n cardColor: Colors.white,\n )\n ),\n child: child!,\n );\n });\n dateSub.value = date;\n },\n child: buildDateTimePicker(\n dateVal != null ? convertDate(dateVal) : ''));\n }),\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"buildDateTimePicker","attrs":{}}],"attrs":{}},{"type":"text","text":"只不過是一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"listTile","attrs":{}}],"attrs":{}},{"type":"text","text":"帶有自定義邊框和日曆圖標:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"Widget buildDateTimePicker(String data) {\n return ListTile(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.circular(10.0),\n side: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n ),\n title: Text(data),\n trailing: const Icon(\n Icons.calendar_today,\n color: AppColors.eggPlant,\n ),\n );\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們還有一個字符串方法可以將日期轉換爲所需的格式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"String convertDate(DateTime dateTime) {\n return DateFormat('dd/MM/yyyy').format(dateTime);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是代碼實現時的樣子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/31/313b45904d4de8466c3832f53c4dec28.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,讓我們回到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"我之前討論過的,我們將如何實現它,以及我們將如何定製它以滿足應用程序的需求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有幾種自定義可能性,討論所有這些都超出了本文的範圍。因此,我會盡量做到具體,只討論其中最重要的部分。當然,也有我親自試驗過的代碼實現,以及可供參考的圖像。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"表日曆","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安裝非常簡單:您需要從這裏複製並粘貼","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"pubspec.yaml","attrs":{}}],"attrs":{}},{"type":"text","text":"文件中的依賴項","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"table_calendar","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最新版本是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"table_calendar: ^3.0.2\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我將把它的構造函數分成三個部分:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"設置","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"根據您的應用程序需求設計日曆樣式","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"將事件添加到日曆","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣您就可以輕鬆理解代碼並知道如何成功實現它。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"第 1 步:設置","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我已用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SingleChildScrollView","attrs":{}}],"attrs":{}},{"type":"text","text":"做我的父小部件,然後在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Card","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件中添加了一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Column","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件,以稍微提升日曆。然後,我在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件中添加了小","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Card","attrs":{}}],"attrs":{}},{"type":"text","text":"部件作爲其子部件:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"SingleChildScrollView(\n child: Column(\n children: [\n Card(\n margin: const EdgeInsets.all(8.0),\n elevation: 5.0,\n shape: const RoundedRectangleBorder(\n borderRadius: BorderRadius.all(\n Radius.circular(10),\n ),\n side: BorderSide( color: AppColors.blackCoffee, width: 2.0),\n ),\n child: TableCalendar(\n // today's date\n focusedDay: _focusedCalendarDate,\n // earliest possible date\n firstDay: _initialCalendarDate,\n // latest allowed date\n lastDay: _lastCalendarDate, \n // default view when displayed\n calendarFormat: CalendarFormat.month, \n // default is Saturday & Sunday but can be set to any day.\n // instead of day, a number can be mentioned as well.\n weekendDays: const [DateTime.sunday, 6],\n // default is Sunday but can be changed according to locale\n startingDayOfWeek: StartingDayOfWeek.monday,\n // height between the day row and 1st date row, default is 16.0\n daysOfWeekHeight: 40.0,\n // height between the date rows, default is 52.0\n rowHeight: 60.0,\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的代碼正在使用一些默認值和一些根據區域設置進行自定義來設置將在移動屏幕上顯示的日曆。我在每個屬性之前添加了註釋以瞭解它的作用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我知道","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件的類文件中已經給出瞭解釋,但有時用更簡單的術語更容易理解屬性。我習慣於閱讀所有內容,理解它,然後我嘗試爲我的讀者簡化,以便他們在實現代碼之前不必遍歷每一行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6c/6cced7d7c489e8e93de631640211e113.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"第 2 步: ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"的樣式","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"好的,所以還有 3 個部分來設計表格日曆。首先是標題,我們有月份的名稱和一個按鈕,可以在周視圖和月視圖之間切換。左右箭頭在月份之間滾動。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據應用程序的主題,您可以自定義所有內容,以便日曆的外觀和感覺,基本上是日曆的整個 UI,與您的應用程序的 UI 相匹配。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再次將代碼拆分爲 3 部分:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"codeinline","content":[{"type":"text","text":"headerStyle","attrs":{}}],"attrs":{}}]},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 日曆標題樣式\nheaderStyle: const HeaderStyle(\n titleTextStyle:\n TextStyle(color: AppColors.babyPowder, fontSize: 20.0),\n decoration: BoxDecoration(\n color: AppColors.eggPlant,\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(10),\n topRight: Radius.circular(10))),\n formatButtonTextStyle:\n TextStyle(color: AppColors.ultraRed, fontSize: 16.0),\n formatButtonDecoration: BoxDecoration(\n color: AppColors.babyPowder,\n borderRadius: BorderRadius.all(\n Radius.circular(5.0),\n ), ),\n leftChevronIcon: Icon(\n Icons.chevron_left,\n color: AppColors.babyPowder,\n size: 28,\n ),\n rightChevronIcon: Icon(\n Icons.chevron_right,\n color: AppColors.babyPowder,\n size: 28,\n ),\n),\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/9449f4954a19ed08d3bca12c3878bfa6.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"標題下方的樣式天數","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這裏,您可以爲週末、工作日和假期設置不同的顏色(如果您已設置):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 日曆日樣式\ndaysOfWeekStyle: const DaysOfWeekStyle(\n // Weekend days color (Sat,Sun)\n weekendStyle: TextStyle(color: AppColors.ultraRed),\n),\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面的代碼中,我爲我在實現","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"小部件時最初設置的週末添加了顏色。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3d/3dd8734b73b187c263d00c8663ac3d30.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"日期樣式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"您可以在此處爲特定的週末日期或假期日期添加顏色。此外,可以自定義當前日期和所選日期的突出顯示顏色。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 日曆日期樣式\ncalendarStyle: const CalendarStyle(\n // Weekend dates color (Sat & Sun Column)\n weekendTextStyle: TextStyle(color: AppColors.ultraRed),\n // highlighted color for today\n todayDecoration: BoxDecoration(\n color: AppColors.eggPlant,\n shape: BoxShape.circle,\n ),\n // highlighted color for selected day\n selectedDecoration: BoxDecoration(\n color: AppColors.blackCoffee,\n shape: BoxShape.circle,\n ),\n),\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/93/9370ec53f872903d6662bd255dd7a7e6.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來的代碼塊是從","attrs":{}},{"type":"link","attrs":{"href":"https://pub.dev/packages/table_calendar","title":"","type":null},"content":[{"type":"text","text":"所提供的官方文件","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalender","attrs":{}}],"attrs":{}}]},{"type":"text","text":"。這是實現所選日期的默認方式。此代碼根據上述自定義顏色突出顯示當前日期和選定日期。沒有更好的方法可以做到這一點,建議","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"selectedDayPredicate: (currentSelectedDate) {\n // as per the documentation 'selectedDayPredicate' needs to determine current selected day.\n return (isSameDay(\n _selectedCalendarDate!, currentSelectedDate));\n},\nonDaySelected: (selectedDay, focusedDay) {\n // as per the documentation\n if (!isSameDay(_selectedCalendarDate, selectedDay)) {\n setState(() {\n _selectedCalendarDate = selectedDay;\n _focusedCalendarDate = focusedDay;\n });\n }\n},\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"第 3 步:將事件添加到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們已經完成了初始化","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"並將其風格化以匹配我們的 UI。剩下的唯一事情就是將事件添加到我們的日曆中,這是一項重要功能。沒有它,我們的日曆只是一份硬拷貝,我們保存在家裏或冰箱上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,我們中的許多人傾向於在日曆上貼上便利貼來指示整個月、一週甚至一天的關鍵事件。在我們的手機上,我們可以將提醒或事件添加到我們的默認日曆應用程序中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我創建了一個模型類,命名","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MyEvents","attrs":{}}],"attrs":{}},{"type":"text","text":"並初始化了兩個 String 變量","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"eventTitle","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"eventDescp","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"class MyEvents {\n final String eventTitle;\n final String eventDescp;\n\n MyEvents({required this.eventTitle, required this.eventDescp});\n\n @override\n String toString() => eventTitle;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我們的CustomCalendarTable","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"文件中,我添加了兩個","attrs":{}}],"attrs":{}},{"type":"text","text":"TextEditingController","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"s、a和一個map,我們將在其中保存我們的事件列表並將其應用於TableCalandar 中的屬性:","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"final titleController = TextEditingController();\nfinal descpController = TextEditingController();\n\nlate Map> mySelectedEvents;\n\n@override\nvoid initState() {\n selectedCalendarDate = _focusedCalendarDate;\n mySelectedEvents = {};\n super.initState();\n}\n\n@override\nvoid dispose() {\n titleController.dispose();\n descpController.dispose();\n super.dispose();\n}\n\nList _listOfDayEvents(DateTime dateTime) {\n return mySelectedEvents[dateTime] ?? [];\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我向我們添加了一個 fab 按鈕,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Scaffold","attrs":{}}],"attrs":{}},{"type":"text","text":"單擊 fab 按鈕時,將出現一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":" ,用戶將在其中輸入事件標題和事件描述。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單擊 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Add","attrs":{}}],"attrs":{}},{"type":"text","text":"按鈕後出現","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":",將在日曆下添加一個事件,並在添加事件的日期看到一個小彩色圓點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我還添加了一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SnackBar","attrs":{}}],"attrs":{}},{"type":"text","text":"以防用戶沒有在標題文本字段或描述文本字段中輸入任何內容。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SnackBar","attrs":{}}],"attrs":{}},{"type":"text","text":"將彈出一條請輸入標題和描述的消息/","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果用戶輸入了標題和描述,則在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"setState","attrs":{}}],"attrs":{}},{"type":"text","text":"方法中檢查所選事件的列表是否不爲空,然後我們將標題和描述添加到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MyEvents","attrs":{}}],"attrs":{}},{"type":"text","text":"模型類並創建","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MyEvents","attrs":{}}],"attrs":{}},{"type":"text","text":".","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加事件後,我們將清除","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Controller","attrs":{}}],"attrs":{}},{"type":"text","text":"s 並關閉","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"_showAddEventDialog() async {\n await showDialog(\n context: context,\n builder: (context) => AlertDialog(\n title: const Text('New Event'),\n content: Column(\n crossAxisAlignment: CrossAxisAlignment.stretch,\n mainAxisSize: MainAxisSize.min,\n children: [\n buildTextField(\n controller: titleController, hint: 'Enter Title'),\n const SizedBox(\n height: 20.0,\n ),\n buildTextField(\n controller: descpController, hint: 'Enter Description'),\n ], ),\n actions: [\n TextButton(\n onPressed: () => Navigator.pop(context),\n child: const Text('Cancel'),),\n TextButton(\n onPressed: () {\n if (titleController.text.isEmpty &&\n descpController.text.isEmpty) {\n ScaffoldMessenger.of(context).showSnackBar(\n const SnackBar(\n content: Text('Please enter title & description'),\n duration: Duration(seconds: 3),\n ), );\n //Navigator.pop(context);\n return;\n } else {\n setState(() {\n if (mySelectedEvents[selectedCalendarDate] != null) {\n mySelectedEvents[selectedCalendarDate]?.add(MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text));\n } else {\n mySelectedEvents[selectedCalendarDate!] = [\n MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text)\n ]; } });\n\n titleController.clear();\n descpController.clear();\n\n Navigator.pop(context);\n return;\n }\n },\n child: const Text('Add'),\n ),\n ],\n ));}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我已經構建了一個自定義文本字段,該字段已在以下內容中初始化","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AlertDialog","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"Widget buildTextField(\n {String? hint, required TextEditingController controller}) {\n return TextField(\n controller: controller,\n textCapitalization: TextCapitalization.words,\n decoration: InputDecoration(\n labelText: hint ?? '',\n focusedBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n enabledBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n ),\n );\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我添加小部件","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"eventLoader","attrs":{}}],"attrs":{}},{"type":"text","text":"下的屬性並向其","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"添加","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_listofDayEvents","attrs":{}}],"attrs":{}},{"type":"text","text":"方法時,一切都彙集在一起","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"// 需要添加這個屬性來顯示事件\neventLoader: _listOfDayEvents,\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就是這樣,我們已經成功地實現了將事件添加到日曆日期並將其顯示在我們的應用程序中的日曆下的方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如我在本文前面提到的,有一些優秀的日曆庫可用,例如 flutter_calendar_carousel 和 syncfusion_flutter_calendar。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的基本實現保持不變。甚至屬性和自定義也與我","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TableCalendar","attrs":{}}],"attrs":{}},{"type":"text","text":"在本文中提到的非常相似。儘管屬性的名稱不同,但功能保持不變。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我試圖包含儘可能多的細節,以幫助希望在其應用程序中集成日曆的任何人,但正如我經常說的,發現則去實驗,這一直是我的座右銘。因此,請使用代碼,如果您需要更多關於這方面的信息,可以關注我。非常感謝!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"最後附上完整代碼","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"dart"},"content":[{"type":"text","text":"class CustomTableCalendar extends StatefulWidget {\n const CustomTableCalendar({Key? key}) : super(key: key);\n\n @override\n _CustomTableCalendarState createState() => _CustomTableCalendarState();\n}\n\nclass _CustomTableCalendarState extends State {\n final todaysDate = DateTime.now();\n var _focusedCalendarDate = DateTime.now();\n final _initialCalendarDate = DateTime(2000);\n final _lastCalendarDate = DateTime(2050);\n DateTime? selectedCalendarDate;\n final titleController = TextEditingController();\n final descpController = TextEditingController();\n\n late Map> mySelectedEvents;\n\n @override\n void initState() {\n selectedCalendarDate = _focusedCalendarDate;\n mySelectedEvents = {};\n super.initState();\n }\n\n @override\n void dispose() {\n titleController.dispose();\n descpController.dispose();\n super.dispose();\n }\n\n List _listOfDayEvents(DateTime dateTime) {\n return mySelectedEvents[dateTime] ?? [];\n }\n\n _showAddEventDialog() async {\n await showDialog(\n context: context,\n builder: (context) => AlertDialog(\n title: const Text('New Event'),\n content: Column(\n crossAxisAlignment: CrossAxisAlignment.stretch,\n mainAxisSize: MainAxisSize.min,\n children: [\n buildTextField(\n controller: titleController, hint: 'Enter Title'),\n const SizedBox(\n height: 20.0,\n ),\n buildTextField(\n controller: descpController, hint: 'Enter Description'),\n ],\n ),\n actions: [\n TextButton(\n onPressed: () => Navigator.pop(context),\n child: const Text('Cancel'),\n ),\n TextButton(\n onPressed: () {\n if (titleController.text.isEmpty &&\n descpController.text.isEmpty) {\n ScaffoldMessenger.of(context).showSnackBar(\n const SnackBar(\n content: Text('Please enter title & description'),\n duration: Duration(seconds: 3),\n ),\n );\n //Navigator.pop(context);\n return;\n } else {\n setState(() {\n if (mySelectedEvents[selectedCalendarDate] != null) {\n mySelectedEvents[selectedCalendarDate]?.add(MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text));\n } else {\n mySelectedEvents[selectedCalendarDate!] = [\n MyEvents(\n eventTitle: titleController.text,\n eventDescp: descpController.text)\n ];\n }\n });\n\n titleController.clear();\n descpController.clear();\n\n Navigator.pop(context);\n return;\n }\n },\n child: const Text('Add'),\n ),\n ],\n ));\n }\n\n Widget buildTextField(\n {String? hint, required TextEditingController controller}) {\n return TextField(\n controller: controller,\n textCapitalization: TextCapitalization.words,\n decoration: InputDecoration(\n labelText: hint ?? '',\n focusedBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n enabledBorder: OutlineInputBorder(\n borderSide: const BorderSide(color: AppColors.eggPlant, width: 1.5),\n borderRadius: BorderRadius.circular(\n 10.0,\n ),\n ),\n ),\n );\n }\n\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: const Text('Custom Calendar'),\n ),\n floatingActionButton: FloatingActionButton.extended(\n onPressed: () => _showAddEventDialog(),\n label: const Text('Add Event'),\n ),\n body: SingleChildScrollView(\n child: Column(\n children: [\n Card(\n margin: const EdgeInsets.all(8.0),\n elevation: 5.0,\n shape: const RoundedRectangleBorder(\n borderRadius: BorderRadius.all(\n Radius.circular(10),\n ),\n side: BorderSide(color: AppColors.blackCoffee, width: 2.0),\n ),\n child: TableCalendar(\n focusedDay: _focusedCalendarDate,\n // today's date\n firstDay: _initialCalendarDate,\n // earliest possible date\n lastDay: _lastCalendarDate,\n // latest allowed date\n calendarFormat: CalendarFormat.month,\n // default view when displayed\n // default is Saturday & Sunday but can be set to any day.\n // instead of day number can be mentioned as well.\n weekendDays: const [DateTime.sunday, 6],\n // default is Sunday but can be changed according to locale\n startingDayOfWeek: StartingDayOfWeek.monday,\n // height between the day row and 1st date row, default is 16.0\n daysOfWeekHeight: 40.0,\n // height between the date rows, default is 52.0\n rowHeight: 60.0,\n // this property needs to be added if we want to show events\n eventLoader: _listOfDayEvents,\n // Calendar Header Styling\n headerStyle: const HeaderStyle(\n titleTextStyle:\n TextStyle(color: AppColors.babyPowder, fontSize: 20.0),\n decoration: BoxDecoration(\n color: AppColors.eggPlant,\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(10),\n topRight: Radius.circular(10))),\n formatButtonTextStyle:\n TextStyle(color: AppColors.ultraRed, fontSize: 16.0),\n formatButtonDecoration: BoxDecoration(\n color: AppColors.babyPowder,\n borderRadius: BorderRadius.all(\n Radius.circular(5.0),\n ),\n ),\n leftChevronIcon: Icon(\n Icons.chevron_left,\n color: AppColors.babyPowder,\n size: 28,\n ),\n rightChevronIcon: Icon(\n Icons.chevron_right,\n color: AppColors.babyPowder,\n size: 28,\n ),\n ),\n // Calendar Days Styling\n daysOfWeekStyle: const DaysOfWeekStyle(\n // Weekend days color (Sat,Sun)\n weekendStyle: TextStyle(color: AppColors.ultraRed),\n ),\n // Calendar Dates styling\n calendarStyle: const CalendarStyle(\n // Weekend dates color (Sat & Sun Column)\n weekendTextStyle: TextStyle(color: AppColors.ultraRed),\n // highlighted color for today\n todayDecoration: BoxDecoration(\n color: AppColors.eggPlant,\n shape: BoxShape.circle,\n ),\n // highlighted color for selected day\n selectedDecoration: BoxDecoration(\n color: AppColors.blackCoffee,\n shape: BoxShape.circle,\n ),\n markerDecoration: BoxDecoration(\n color: AppColors.ultraRed, shape: BoxShape.circle),\n ),\n selectedDayPredicate: (currentSelectedDate) {\n // as per the documentation 'selectedDayPredicate' needs to determine\n // current selected day\n return (isSameDay(\n selectedCalendarDate!, currentSelectedDate));\n },\n onDaySelected: (selectedDay, focusedDay) {\n // as per the documentation\n if (!isSameDay(selectedCalendarDate, selectedDay)) {\n setState(() {\n selectedCalendarDate = selectedDay;\n _focusedCalendarDate = focusedDay;\n });\n }\n },\n ),\n ),\n ..._listOfDayEvents(selectedCalendarDate!).map(\n (myEvents) => ListTile(\n leading: const Icon(\n Icons.done,\n color: AppColors.eggPlant,\n ),\n title: Padding(\n padding: const EdgeInsets.only(bottom: 8.0),\n child: Text('Event Title: ${myEvents.eventTitle}'),\n ),\n subtitle: Text('Description: ${myEvents.eventDescp}'),\n ),\n ),\n ],\n ),\n ),\n );\n }\n}\nclass MyEvents {\n final String eventTitle;\n final String eventDescp;\n\n MyEvents({required this.eventTitle, required this.eventDescp});\n\n @override\n String toString() => eventTitle;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章