leave_behind_demo.dart 7.08 KB
Newer Older
1 2 3 4
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'package:collection/collection.dart' show lowerBound;

7
import 'package:flutter/material.dart';
8
import 'package:flutter/semantics.dart';
9

10 11
import '../../gallery/demo.dart';

12 13 14 15 16 17 18
enum LeaveBehindDemoAction {
  reset,
  horizontalSwipe,
  leftSwipe,
  rightSwipe
}

Adam Barth's avatar
Adam Barth committed
19
class LeaveBehindItem implements Comparable<LeaveBehindItem> {
20 21 22 23 24 25 26 27 28
  LeaveBehindItem({ this.index, this.name, this.subject, this.body });

  LeaveBehindItem.from(LeaveBehindItem item)
    : index = item.index, name = item.name, subject = item.subject, body = item.body;

  final int index;
  final String name;
  final String subject;
  final String body;
Adam Barth's avatar
Adam Barth committed
29 30 31

  @override
  int compareTo(LeaveBehindItem other) => index.compareTo(other.index);
32 33
}

34
class LeaveBehindDemo extends StatefulWidget {
35
  const LeaveBehindDemo({ Key key }) : super(key: key);
36

37
  static const String routeName = '/material/leave-behind';
38

39
  @override
40
  LeaveBehindDemoState createState() => LeaveBehindDemoState();
41 42 43
}

class LeaveBehindDemoState extends State<LeaveBehindDemo> {
44
  static final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
45 46 47 48
  DismissDirection _dismissDirection = DismissDirection.horizontal;
  List<LeaveBehindItem> leaveBehindItems;

  void initListItems() {
49 50
    leaveBehindItems = List<LeaveBehindItem>.generate(16, (int index) {
      return LeaveBehindItem(
51 52 53 54 55 56 57 58
        index: index,
        name: 'Item $index Sender',
        subject: 'Subject: $index',
        body: "[$index] first line of the message's body..."
      );
    });
  }

59
  @override
60 61 62 63 64 65
  void initState() {
    super.initState();
    initListItems();
  }

  void handleDemoAction(LeaveBehindDemoAction action) {
Ian Hickson's avatar
Ian Hickson committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    setState(() {
      switch (action) {
        case LeaveBehindDemoAction.reset:
          initListItems();
          break;
        case LeaveBehindDemoAction.horizontalSwipe:
          _dismissDirection = DismissDirection.horizontal;
          break;
        case LeaveBehindDemoAction.leftSwipe:
          _dismissDirection = DismissDirection.endToStart;
          break;
        case LeaveBehindDemoAction.rightSwipe:
          _dismissDirection = DismissDirection.startToEnd;
          break;
      }
    });
82 83
  }

84
  void handleUndo(LeaveBehindItem item) {
85
    final int insertionIndex = lowerBound(leaveBehindItems, item);
86 87 88 89 90
    setState(() {
      leaveBehindItems.insert(insertionIndex, item);
    });
  }

91 92 93 94
  void _handleArchive(LeaveBehindItem item) {
    setState(() {
      leaveBehindItems.remove(item);
    });
95 96 97
    _scaffoldKey.currentState.showSnackBar(SnackBar(
      content: Text('You archived item ${item.index}'),
      action: SnackBarAction(
98 99
        label: 'UNDO',
        onPressed: () { handleUndo(item); }
100
      )
101 102 103 104 105 106 107
    ));
  }

  void _handleDelete(LeaveBehindItem item) {
    setState(() {
      leaveBehindItems.remove(item);
    });
108 109 110
    _scaffoldKey.currentState.showSnackBar(SnackBar(
      content: Text('You deleted item ${item.index}'),
      action: SnackBarAction(
111 112 113 114
        label: 'UNDO',
        onPressed: () { handleUndo(item); }
      )
    ));
115 116
  }

117
  @override
118
  Widget build(BuildContext context) {
119 120
    Widget body;
    if (leaveBehindItems.isEmpty) {
121 122
      body = Center(
        child: RaisedButton(
123 124 125 126 127
          onPressed: () => handleDemoAction(LeaveBehindDemoAction.reset),
          child: const Text('Reset the list'),
        ),
      );
    } else {
128
      body = ListView(
129
        children: leaveBehindItems.map<Widget>((LeaveBehindItem item) {
130
          return _LeaveBehindListItem(
131 132 133 134 135 136 137 138 139
            item: item,
            onArchive: _handleArchive,
            onDelete: _handleDelete,
            dismissDirection: _dismissDirection,
          );
        }).toList()
      );
    }

140
    return Scaffold(
141
      key: _scaffoldKey,
142
      appBar: AppBar(
143
        title: const Text('Swipe to dismiss'),
144
        actions: <Widget>[
145
          MaterialDemoDocumentationButton(LeaveBehindDemo.routeName),
146
          PopupMenuButton<LeaveBehindDemoAction>(
147
            onSelected: handleDemoAction,
148
            itemBuilder: (BuildContext context) => <PopupMenuEntry<LeaveBehindDemoAction>>[
149
              const PopupMenuItem<LeaveBehindDemoAction>(
150
                value: LeaveBehindDemoAction.reset,
151
                child: Text('Reset the list')
152
              ),
153
              const PopupMenuDivider(),
154
              CheckedPopupMenuItem<LeaveBehindDemoAction>(
155 156
                value: LeaveBehindDemoAction.horizontalSwipe,
                checked: _dismissDirection == DismissDirection.horizontal,
Josh Soref's avatar
Josh Soref committed
157
                child: const Text('Horizontal swipe')
158
              ),
159
              CheckedPopupMenuItem<LeaveBehindDemoAction>(
160
                value: LeaveBehindDemoAction.leftSwipe,
161
                checked: _dismissDirection == DismissDirection.endToStart,
162
                child: const Text('Only swipe left')
163
              ),
164
              CheckedPopupMenuItem<LeaveBehindDemoAction>(
165
                value: LeaveBehindDemoAction.rightSwipe,
166
                checked: _dismissDirection == DismissDirection.startToEnd,
167
                child: const Text('Only swipe right')
168 169 170 171 172
              )
            ]
          )
        ]
      ),
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
      body: body,
    );
  }
}

class _LeaveBehindListItem extends StatelessWidget {
  const _LeaveBehindListItem({
    Key key,
    @required this.item,
    @required this.onArchive,
    @required this.onDelete,
    @required this.dismissDirection,
  }) : super(key: key);

  final LeaveBehindItem item;
  final DismissDirection dismissDirection;
  final void Function(LeaveBehindItem) onArchive;
  final void Function(LeaveBehindItem) onDelete;

  void _handleArchive() {
    onArchive(item);
  }

  void _handleDelete() {
    onDelete(item);
  }

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
203
    return Semantics(
204 205 206 207
      customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
        const CustomSemanticsAction(label: 'Archive'): _handleArchive,
        const CustomSemanticsAction(label: 'Delete'): _handleDelete,
      },
208 209
      child: Dismissible(
        key: ObjectKey(item),
210 211 212 213 214 215 216
        direction: dismissDirection,
        onDismissed: (DismissDirection direction) {
          if (direction == DismissDirection.endToStart)
            _handleArchive();
          else
            _handleDelete();
        },
217
        background: Container(
218 219
          color: theme.primaryColor,
          child: const ListTile(
220
            leading: Icon(Icons.delete, color: Colors.white, size: 36.0)
221 222
          )
        ),
223
        secondaryBackground: Container(
224 225
          color: theme.primaryColor,
          child: const ListTile(
226
            trailing: Icon(Icons.archive, color: Colors.white, size: 36.0)
227 228
          )
        ),
229 230
        child: Container(
          decoration: BoxDecoration(
231
            color: theme.canvasColor,
232
            border: Border(bottom: BorderSide(color: theme.dividerColor))
233
          ),
234 235 236
          child: ListTile(
            title: Text(item.name),
            subtitle: Text('${item.subject}\n${item.body}'),
237 238 239 240
            isThreeLine: true
          ),
        ),
      ),
241 242 243
    );
  }
}