• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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