[READ-ONLY] Mirror of https://github.com/probablykasper/notifier. Android app for scheduling notifications
android app flutter notifications
0

Configure Feed

Select the types of activity you want to include in your feed.

Add edit dialog

Kasper (May 30, 2022, 10:08 PM +0200) d9fd9cec ca1eb37a

+398 -39
+365
lib/edit_dialog.dart
··· 1 + import 'package:flutter/cupertino.dart'; 2 + import 'package:get/get.dart' show Get, GetNavigation; 3 + import 'package:flutter/material.dart' 4 + show 5 + Colors, 6 + Column, 7 + Container, 8 + DropdownButton, 9 + DropdownMenuItem, 10 + EdgeInsets, 11 + InputDecoration, 12 + MaterialButton, 13 + MaterialTapTargetSize, 14 + Row, 15 + SimpleDialog, 16 + Text, 17 + TextFormField, 18 + Theme, 19 + Widget; 20 + import 'package:notifier/notification_items.dart' show NotificationItem, Repeat; 21 + import 'package:notifier/theme.dart'; 22 + 23 + class EditDialog extends StatefulWidget { 24 + final NotificationItem item; 25 + final bool editMode; 26 + final void Function(NotificationItem item) onSave; 27 + 28 + EditDialog({ 29 + required this.item, 30 + required this.editMode, 31 + required this.onSave, 32 + }); 33 + 34 + @override 35 + EditDialogState createState() { 36 + return EditDialogState(); 37 + } 38 + } 39 + 40 + class EditDialogState extends State<EditDialog> { 41 + final GlobalKey<FormState> formKey = GlobalKey<FormState>(); 42 + 43 + @override 44 + Widget build(context) { 45 + return Form( 46 + key: formKey, 47 + child: SimpleDialog( 48 + // everything should have a horizontal padding of 24 49 + contentPadding: const EdgeInsets.all(0), 50 + title: widget.editMode 51 + ? const Text('Edit notification') 52 + : const Text('Add notification'), 53 + children: <Widget>[ 54 + Container( 55 + padding: const EdgeInsets.only(top: 12, bottom: 0), 56 + width: 600, 57 + child: Column( 58 + crossAxisAlignment: CrossAxisAlignment.start, 59 + children: <Widget>[ 60 + //* TITLE 61 + Padding( 62 + padding: const EdgeInsets.only( 63 + left: 24, right: 24, top: 0, bottom: 8), 64 + child: TextFormField( 65 + initialValue: widget.item.title, 66 + textInputAction: TextInputAction.next, 67 + decoration: const InputDecoration( 68 + labelText: 'Title', 69 + contentPadding: 70 + EdgeInsets.symmetric(horizontal: 12, vertical: 8), 71 + ), 72 + autovalidateMode: AutovalidateMode.onUserInteraction, 73 + validator: (value) { 74 + if (value == '') return 'Write something, bro'; 75 + return null; 76 + }, 77 + onChanged: (String newValue) { 78 + widget.item.title = newValue; 79 + }, 80 + ), 81 + ), 82 + //* DESCRIPTION 83 + Padding( 84 + padding: const EdgeInsets.only( 85 + left: 24, right: 24, top: 0, bottom: 8), 86 + child: TextFormField( 87 + initialValue: widget.item.description, 88 + keyboardType: TextInputType.multiline, 89 + maxLines: null, 90 + decoration: const InputDecoration( 91 + labelText: 'Description', 92 + contentPadding: 93 + EdgeInsets.symmetric(horizontal: 12, vertical: 8), 94 + ), 95 + onChanged: (String newValue) { 96 + widget.item.description = newValue; 97 + }, 98 + ), 99 + ), 100 + //* CANNOT BE SWIPED AWAY? 101 + // ListTile( 102 + // // this is not a SwitchListTile because that doesn't support padding 103 + // contentPadding: EdgeInsets.symmetric(horizontal: 24), 104 + // title: Text('Notification cannot be swiped away'), 105 + // onTap: () { 106 + // model.item['noSwipeAway'] = !model.item['noSwipeAway']; 107 + // model.rebuild(); 108 + // }, 109 + // trailing: Switch( 110 + // value: model.item['noSwipeAway'], 111 + // onChanged: (bool newValue) { 112 + // model.item['noSwipeAway'] = newValue; 113 + // model.rebuild(); 114 + // }), 115 + // ), 116 + //* TIME 117 + // ListTile( 118 + // contentPadding: EdgeInsets.symmetric(horizontal: 24), 119 + // leading: Icon(Icons.calendar_today), 120 + // title: Text('Time', 121 + // style: TextStyle(color: themeModel.textColor)), 122 + // isThreeLine: timeHasPassed(), 123 + // // subtitle: Text( 124 + // // DateFormat("MMMM d, y 'at' h:mm a").format( 125 + // // DateTime.fromMillisecondsSinceEpoch(model.item['date']), 126 + // // ), 127 + // // ), 128 + // subtitle: RichText( 129 + // text: TextSpan( 130 + // children: [ 131 + // TextSpan( 132 + // text: DateFormat("MMMM d, y 'at' h:mm a").format( 133 + // DateTime.fromMillisecondsSinceEpoch( 134 + // model.item['date']), 135 + // ), 136 + // style: TextStyle( 137 + // color: themeModel.textColor, fontFamily: 'Jost'), 138 + // ), 139 + // TextSpan( 140 + // text: !timeHasPassed() ? '' : '\nTime has passed', 141 + // style: TextStyle( 142 + // color: themeModel.errorText, fontFamily: 'Jost'), 143 + // ), 144 + // ], 145 + // ), 146 + // ), 147 + // onTap: () async { 148 + // descriptionFocusNode.unfocus(); 149 + // print('[notifier] Selecting date'); 150 + // await _pickDateTime(context, model); 151 + // final notificationItemModel = 152 + // ScopedModel.of<NotificationDialogModel>(context); 153 + // notificationItemModel.rebuild(); 154 + // }, 155 + // ), 156 + Container( 157 + padding: const EdgeInsets.symmetric(horizontal: 24), 158 + child: Row( 159 + children: <Widget>[ 160 + //* REPEAT 161 + FormField( 162 + initialValue: widget.item.repeat, 163 + onSaved: (newValue) { 164 + // model.item['repeat'] = newValue; 165 + }, 166 + builder: (FormFieldState state) { 167 + return DropdownButton<Repeat>( 168 + onChanged: (newValue) { 169 + print(newValue); 170 + if (newValue != null) { 171 + setState(() { 172 + widget.item.repeat = newValue; 173 + }); 174 + } 175 + }, 176 + value: widget.item.repeat, 177 + elevation: 16, 178 + // style: TextStyle( 179 + // fontFamily: 'Jost', 180 + // color: themeModel.textColor, 181 + // fontSize: 15, 182 + // ), 183 + items: const [ 184 + DropdownMenuItem( 185 + value: Repeat.never, 186 + child: Text("Doesn't Repeat"), 187 + ), 188 + DropdownMenuItem( 189 + value: Repeat.daily, 190 + child: Text("Repeat Daily"), 191 + ), 192 + DropdownMenuItem( 193 + value: Repeat.weekly, 194 + child: Text("Repeat Weekly"), 195 + ), 196 + DropdownMenuItem( 197 + value: Repeat.monthly, 198 + child: Text("Repeat Monthly"), 199 + ), 200 + DropdownMenuItem( 201 + value: Repeat.yearly, 202 + child: Text("Repeat Yearly"), 203 + ), 204 + ], 205 + ); 206 + }, 207 + ), 208 + Container(width: 6), 209 + // (() { 210 + // if (model.item['repeat'] == 'never') { 211 + // return Container(); 212 + // } else { 213 + // return Text('every', style: TextStyle(fontSize: 15)); 214 + // } 215 + // })(), 216 + //* REPEAT EVERY 217 + Container(width: 6), 218 + // (() { 219 + // if (model.item['repeat'] == 'never') { 220 + // return Container(); 221 + // } else { 222 + // return Container( 223 + // width: 35, 224 + // child: TextFormField( 225 + // initialValue: 226 + // model.item['repeatEvery'].toString(), 227 + // onSaved: (String newValue) { 228 + // if (newValue == '') 229 + // model.item['repeatEvery'] = 1; 230 + // else 231 + // model.item['repeatEvery'] = 232 + // int.parse(newValue); 233 + // }, 234 + // textAlign: TextAlign.center, 235 + // keyboardType: TextInputType.numberWithOptions( 236 + // signed: false, 237 + // decimal: false, 238 + // ), 239 + // inputFormatters: [ 240 + // BlacklistingTextInputFormatter(RegExp('^0\$')), 241 + // WhitelistingTextInputFormatter.digitsOnly, 242 + // LengthLimitingTextInputFormatter(3), 243 + // ], 244 + // decoration: InputDecoration( 245 + // hintText: '1', 246 + // contentPadding: 247 + // EdgeInsets.symmetric(vertical: 4), 248 + // ), 249 + // ), 250 + // ); 251 + // } 252 + // })(), 253 + Container(width: 6), 254 + // (() { 255 + // if (model.item['repeat'] == 'daily') { 256 + // return Text('days', style: TextStyle(fontSize: 15)); 257 + // } else if (model.item['repeat'] == 'weekly') { 258 + // return Text('weeks', style: TextStyle(fontSize: 15)); 259 + // } else if (model.item['repeat'] == 'monthly') { 260 + // return Text('months', style: TextStyle(fontSize: 15)); 261 + // } else if (model.item['repeat'] == 'yearly') { 262 + // return Text('years', style: TextStyle(fontSize: 15)); 263 + // } else { 264 + // return Container(); 265 + // } 266 + // })(), 267 + ], 268 + ), 269 + ), 270 + //* WEEKDAY CHECKBOXES 271 + // Container( 272 + // padding: EdgeInsets.symmetric(horizontal: 24), 273 + // child: (() { 274 + // if (model.item['repeat'] == 'weekly') { 275 + // List<LetterCheckbox> checkboxes = []; 276 + // model.item['weekdays'].asMap().forEach((index, valuex) { 277 + // bool value = valuex; 278 + // checkboxes.add( 279 + // LetterCheckbox( 280 + // text: 'MTWTFSS'[index], 281 + // value: value, 282 + // onChanged: (bool newValue) { 283 + // model.item['weekdays'][index] = newValue; 284 + // print('[notifier] Updated selected weekdays: ' + 285 + // model.item['weekdays'].toString()); 286 + // model.rebuild(); 287 + // }, 288 + // ), 289 + // ); 290 + // }); 291 + // return Wrap(children: checkboxes); 292 + // } else { 293 + // return Container(); 294 + // } 295 + // })(), 296 + // ) 297 + ], 298 + ), 299 + ), 300 + Padding( 301 + padding: 302 + const EdgeInsets.only(left: 12, right: 12, top: 24, bottom: 12), 303 + child: Row( 304 + mainAxisAlignment: MainAxisAlignment.end, 305 + children: <Widget>[ 306 + //* DELETE 307 + // (() { 308 + // if (mode == 'edit') 309 + // return Padding( 310 + // padding: EdgeInsets.only(right: 12), 311 + // child: MaterialButton( 312 + // highlightColor: themeModel.highlightColor, 313 + // splashColor: Colors.transparent, 314 + // onPressed: () { 315 + // listModel.delete(model.item['id']); 316 + // Navigator.of(context).pop(); 317 + // }, 318 + // child: 319 + // Text('Delete', style: themeModel.buttonTextStyle), 320 + // materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 321 + // ), 322 + // ); 323 + // else 324 + // return Container(); 325 + // })(), 326 + //* CANCEL 327 + MaterialButton( 328 + splashColor: Colors.transparent, 329 + onPressed: () { 330 + Get.back(); 331 + }, 332 + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 333 + child: const Text('Cancel'), 334 + ), 335 + Container(width: 12), 336 + //* SAVE 337 + MaterialButton( 338 + splashColor: Colors.transparent, 339 + elevation: 0, 340 + // textColor: !themeModel.darkMode && !saveDisabled() 341 + // ? Colors.white 342 + // : null, 343 + onPressed: () { 344 + // titleHasChanged = true; 345 + if (formKey.currentState != null && 346 + formKey.currentState!.validate()) { 347 + formKey.currentState!.save(); 348 + widget.onSave(widget.item); 349 + Get.back(); 350 + } 351 + }, 352 + color: Theme.of(context).custom.primaryButtonColor, 353 + disabledColor: 354 + Theme.of(context).custom.primaryButtonDisabledColor, 355 + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 356 + child: const Text('Save'), 357 + ), 358 + ], 359 + ), 360 + ), 361 + ], 362 + ), 363 + ); 364 + } 365 + }
+25 -34
lib/list_page.dart
··· 1 1 import 'package:flutter/widgets.dart'; 2 2 import 'package:get/get.dart' 3 - show 4 - ExtensionDialog, 5 - Get, 6 - GetNavigation, 7 - GetxController, 8 - Inst, 9 - Obx, 10 - RxInt, 11 - StringExtension; 3 + show ExtensionDialog, Get, GetxController, Inst, Obx; 12 4 import 'package:flutter/material.dart' 13 5 show 14 - AppBar, 15 - Center, 16 6 Colors, 17 7 Column, 18 8 Container, ··· 20 10 CustomScrollView, 21 11 Divider, 22 12 EdgeInsets, 23 - ElevatedButton, 24 13 Expanded, 25 14 FlexibleSpaceBar, 26 15 FloatingActionButton, ··· 32 21 Row, 33 22 Scaffold, 34 23 SliverAppBar, 35 - SliverChildListDelegate, 36 24 SliverList, 37 25 StatelessWidget, 38 26 Switch, ··· 41 29 Theme, 42 30 Widget; 43 31 import 'package:get/state_manager.dart'; 32 + import 'package:notifier/edit_dialog.dart'; 44 33 import 'notification_items.dart' show NotificationItem, Repeat; 45 34 import 'theme.dart' show CustomTheme, toggleDarkMode; 46 35 ··· 58 47 floatingActionButton: FloatingActionButton( 59 48 child: const Icon(Icons.add), 60 49 onPressed: () { 61 - print("Add notification item"); 62 - c.items.add(NotificationItem( 63 - title: 'Yo', 64 - description: 'Ddd', 50 + Get.dialog(EditDialog( 51 + item: NotificationItem( 52 + title: '', 53 + description: '', 54 + disabled: false, 55 + repeat: Repeat.never, 56 + ), 57 + editMode: false, 58 + onSave: (item) { 59 + c.items.add(item); 60 + }, 65 61 )); 66 - c.refresh(); 67 62 }), 68 63 body: CustomScrollView( 69 64 slivers: <Widget>[ ··· 73 68 IconButton( 74 69 icon: const Icon(Icons.wb_sunny), 75 70 onPressed: () { 76 - print("Toggle dark mode"); 77 71 toggleDarkMode(); 78 72 }, 79 73 color: Colors.white, ··· 118 112 // '[notifier] Running setNotifications() before opening edit dialog'); 119 113 // await listModel.setNotifications(appIsOpen: true); 120 114 // } 121 - print("[notifier] Opening edit dialog"); 122 - // return showDialog( 123 - // context: context, 124 - // builder: (BuildContext context) { 125 - // return NotificationDialog( 126 - // mode: 'edit', 127 - // // clone the item so it changes to it won't be saved 128 - // initialItem: Map<String, dynamic>.from(item), 129 - // listModel: listModel, 130 - // ); 131 - // }, 132 - // ); 115 + Get.dialog(EditDialog( 116 + item: NotificationItem.fromJson(c.items[index].toJson()), 117 + editMode: true, 118 + onSave: (item) { 119 + c.items[index] = item; 120 + }, 121 + )); 133 122 }, 134 123 child: Column( 135 124 children: <Widget>[ 136 125 Container( 137 - padding: EdgeInsets.symmetric(vertical: 16, horizontal: 16), 126 + padding: 127 + const EdgeInsets.symmetric(vertical: 16, horizontal: 16), 138 128 child: Row( 139 129 children: <Widget>[ 140 130 Container(width: 8), ··· 151 141 ), 152 142 Text( 153 143 c.items[index].description, 154 - style: const TextStyle( 144 + style: TextStyle( 155 145 fontSize: 14, 156 146 fontWeight: FontWeight.w500, 157 - // color: themeModel.descriptionColor, 147 + color: 148 + Theme.of(context).custom.descriptionColor, 158 149 ), 159 150 ), 160 151 ],
+4 -4
lib/notification_items.dart
··· 23 23 // int firedCount; 24 24 25 25 NotificationItem({ 26 - this.title = '', 27 - this.description = '', 28 - this.disabled = false, 29 - this.repeat = Repeat.never, 26 + required this.title, 27 + required this.description, 28 + required this.disabled, 29 + required this.repeat, 30 30 }); 31 31 32 32 NotificationItem.fromJson(Map<String, dynamic> json)
+4 -1
lib/theme.dart
··· 12 12 TextStyle, 13 13 ThemeData, 14 14 ThemeMode; 15 - import 'package:flutter/scheduler.dart' show SchedulerBinding; 16 15 import 'package:get/get.dart' show Get, GetNavigation; 17 16 import 'main.dart' show prefsFuture; 18 17 ··· 141 140 class CustomThemeData { 142 141 Color? appBarBackgroundColor; 143 142 Color? descriptionColor; 143 + Color? primaryButtonColor; 144 + Color? primaryButtonDisabledColor = white.c4.withOpacity(0.5); 144 145 TextStyle? appTitleStyle; 145 146 146 147 CustomThemeData light() { 147 148 appBarBackgroundColor = blue; 148 149 descriptionColor = white.c6; 150 + primaryButtonColor = blue; 149 151 appTitleStyle = const TextStyle(color: Colors.white); 150 152 return this; 151 153 } ··· 153 155 CustomThemeData dark() { 154 156 appBarBackgroundColor = grey.c2; 155 157 descriptionColor = grey.c6; 158 + primaryButtonColor = grey.c4; 156 159 appTitleStyle = const TextStyle(color: blue); 157 160 return this; 158 161 }