1// Copyright 2016 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import 'dart:async'; 6 7import 'package:flutter/material.dart'; 8import 'package:intl/intl.dart'; 9 10// This demo is based on 11// https://material.io/design/components/dialogs.html#full-screen-dialog 12 13enum DismissDialogAction { 14 cancel, 15 discard, 16 save, 17} 18 19class DateTimeItem extends StatelessWidget { 20 DateTimeItem({ Key key, DateTime dateTime, @required this.onChanged }) 21 : assert(onChanged != null), 22 date = DateTime(dateTime.year, dateTime.month, dateTime.day), 23 time = TimeOfDay(hour: dateTime.hour, minute: dateTime.minute), 24 super(key: key); 25 26 final DateTime date; 27 final TimeOfDay time; 28 final ValueChanged<DateTime> onChanged; 29 30 @override 31 Widget build(BuildContext context) { 32 final ThemeData theme = Theme.of(context); 33 34 return DefaultTextStyle( 35 style: theme.textTheme.subhead, 36 child: Row( 37 children: <Widget>[ 38 Expanded( 39 child: Container( 40 padding: const EdgeInsets.symmetric(vertical: 8.0), 41 decoration: BoxDecoration( 42 border: Border(bottom: BorderSide(color: theme.dividerColor)) 43 ), 44 child: InkWell( 45 onTap: () { 46 showDatePicker( 47 context: context, 48 initialDate: date, 49 firstDate: date.subtract(const Duration(days: 30)), 50 lastDate: date.add(const Duration(days: 30)), 51 ) 52 .then<void>((DateTime value) { 53 if (value != null) 54 onChanged(DateTime(value.year, value.month, value.day, time.hour, time.minute)); 55 }); 56 }, 57 child: Row( 58 mainAxisAlignment: MainAxisAlignment.spaceBetween, 59 children: <Widget>[ 60 Text(DateFormat('EEE, MMM d yyyy').format(date)), 61 const Icon(Icons.arrow_drop_down, color: Colors.black54), 62 ], 63 ), 64 ), 65 ), 66 ), 67 Container( 68 margin: const EdgeInsets.only(left: 8.0), 69 padding: const EdgeInsets.symmetric(vertical: 8.0), 70 decoration: BoxDecoration( 71 border: Border(bottom: BorderSide(color: theme.dividerColor)) 72 ), 73 child: InkWell( 74 onTap: () { 75 showTimePicker( 76 context: context, 77 initialTime: time, 78 ) 79 .then<void>((TimeOfDay value) { 80 if (value != null) 81 onChanged(DateTime(date.year, date.month, date.day, value.hour, value.minute)); 82 }); 83 }, 84 child: Row( 85 children: <Widget>[ 86 Text('${time.format(context)}'), 87 const Icon(Icons.arrow_drop_down, color: Colors.black54), 88 ], 89 ), 90 ), 91 ), 92 ], 93 ), 94 ); 95 } 96} 97 98class FullScreenDialogDemo extends StatefulWidget { 99 @override 100 FullScreenDialogDemoState createState() => FullScreenDialogDemoState(); 101} 102 103class FullScreenDialogDemoState extends State<FullScreenDialogDemo> { 104 DateTime _fromDateTime = DateTime.now(); 105 DateTime _toDateTime = DateTime.now(); 106 bool _allDayValue = false; 107 bool _saveNeeded = false; 108 bool _hasLocation = false; 109 bool _hasName = false; 110 String _eventName; 111 112 Future<bool> _onWillPop() async { 113 _saveNeeded = _hasLocation || _hasName || _saveNeeded; 114 if (!_saveNeeded) 115 return true; 116 117 final ThemeData theme = Theme.of(context); 118 final TextStyle dialogTextStyle = theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color); 119 120 return await showDialog<bool>( 121 context: context, 122 builder: (BuildContext context) { 123 return AlertDialog( 124 content: Text( 125 'Discard new event?', 126 style: dialogTextStyle, 127 ), 128 actions: <Widget>[ 129 FlatButton( 130 child: const Text('CANCEL'), 131 onPressed: () { 132 Navigator.of(context).pop(false); // Pops the confirmation dialog but not the page. 133 }, 134 ), 135 FlatButton( 136 child: const Text('DISCARD'), 137 onPressed: () { 138 Navigator.of(context).pop(true); // Returning true to _onWillPop will pop again. 139 }, 140 ), 141 ], 142 ); 143 }, 144 ) ?? false; 145 } 146 147 @override 148 Widget build(BuildContext context) { 149 final ThemeData theme = Theme.of(context); 150 151 return Scaffold( 152 appBar: AppBar( 153 title: Text(_hasName ? _eventName : 'Event Name TBD'), 154 actions: <Widget> [ 155 FlatButton( 156 child: Text('SAVE', style: theme.textTheme.body1.copyWith(color: Colors.white)), 157 onPressed: () { 158 Navigator.pop(context, DismissDialogAction.save); 159 }, 160 ), 161 ], 162 ), 163 body: Form( 164 onWillPop: _onWillPop, 165 child: Scrollbar( 166 child: ListView( 167 padding: const EdgeInsets.all(16.0), 168 children: <Widget>[ 169 Container( 170 padding: const EdgeInsets.symmetric(vertical: 8.0), 171 alignment: Alignment.bottomLeft, 172 child: TextField( 173 decoration: const InputDecoration( 174 labelText: 'Event name', 175 filled: true, 176 ), 177 style: theme.textTheme.headline, 178 onChanged: (String value) { 179 setState(() { 180 _hasName = value.isNotEmpty; 181 if (_hasName) { 182 _eventName = value; 183 } 184 }); 185 }, 186 ), 187 ), 188 Container( 189 padding: const EdgeInsets.symmetric(vertical: 8.0), 190 alignment: Alignment.bottomLeft, 191 child: TextField( 192 decoration: const InputDecoration( 193 labelText: 'Location', 194 hintText: 'Where is the event?', 195 filled: true, 196 ), 197 onChanged: (String value) { 198 setState(() { 199 _hasLocation = value.isNotEmpty; 200 }); 201 }, 202 ), 203 ), 204 Column( 205 crossAxisAlignment: CrossAxisAlignment.start, 206 children: <Widget>[ 207 Text('From', style: theme.textTheme.caption), 208 DateTimeItem( 209 dateTime: _fromDateTime, 210 onChanged: (DateTime value) { 211 setState(() { 212 _fromDateTime = value; 213 _saveNeeded = true; 214 }); 215 }, 216 ), 217 ], 218 ), 219 Column( 220 crossAxisAlignment: CrossAxisAlignment.start, 221 children: <Widget>[ 222 Text('To', style: theme.textTheme.caption), 223 DateTimeItem( 224 dateTime: _toDateTime, 225 onChanged: (DateTime value) { 226 setState(() { 227 _toDateTime = value; 228 _saveNeeded = true; 229 }); 230 }, 231 ), 232 const Text('All-day'), 233 ], 234 ), 235 Container( 236 decoration: BoxDecoration( 237 border: Border(bottom: BorderSide(color: theme.dividerColor)) 238 ), 239 child: Row( 240 children: <Widget> [ 241 Checkbox( 242 value: _allDayValue, 243 onChanged: (bool value) { 244 setState(() { 245 _allDayValue = value; 246 _saveNeeded = true; 247 }); 248 }, 249 ), 250 const Text('All-day'), 251 ], 252 ), 253 ), 254 ] 255 .map<Widget>((Widget child) { 256 return Container( 257 padding: const EdgeInsets.symmetric(vertical: 8.0), 258 height: 96.0, 259 child: child, 260 ); 261 }) 262 .toList(), 263 ), 264 ), 265 ), 266 ); 267 } 268} 269