main.dart 19.9 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

void main() {
  runApp(
10
    ComplexLayoutApp()
11 12 13
  );
}

14 15
enum ScrollMode { complex, tile }

16 17
class ComplexLayoutApp extends StatefulWidget {
  @override
18
  ComplexLayoutAppState createState() => ComplexLayoutAppState();
19

20
  static ComplexLayoutAppState of(BuildContext context) => context.findAncestorStateOfType<ComplexLayoutAppState>();
21 22 23 24 25
}

class ComplexLayoutAppState extends State<ComplexLayoutApp> {
  @override
  Widget build(BuildContext context) {
26 27
    return MaterialApp(
      theme: lightTheme ? ThemeData.light() : ThemeData.dark(),
28
      title: 'Advanced Layout',
29
      home: scrollMode == ScrollMode.complex ? const ComplexLayout() : const TileScrollLayout());
30 31 32 33
  }

  bool _lightTheme = true;
  bool get lightTheme => _lightTheme;
34
  set lightTheme(bool value) {
35 36 37 38
    setState(() {
      _lightTheme = value;
    });
  }
39

40 41 42 43 44 45 46 47
  ScrollMode _scrollMode = ScrollMode.complex;
  ScrollMode get scrollMode => _scrollMode;
  set scrollMode(ScrollMode mode) {
    setState(() {
      _scrollMode = mode;
    });
  }

48 49 50 51 52
  void toggleAnimationSpeed() {
    setState(() {
      timeDilation = (timeDilation != 1.0) ? 1.0 : 5.0;
    });
  }
53 54
}

55 56 57 58 59
class TileScrollLayout extends StatelessWidget {
  const TileScrollLayout({ Key key }) : super(key: key);

  @override
  Widget build(BuildContext context) {
60 61 62
    return Scaffold(
      appBar: AppBar(title: const Text('Tile Scrolling Layout')),
      body: ListView.builder(
63 64 65
        key: const Key('tiles-scroll'),
        itemCount: 200,
        itemBuilder: (BuildContext context, int index) {
66
          return Padding(
67
            padding: const EdgeInsets.all(5.0),
68
            child: Material(
69
              elevation: (index % 5 + 1).toDouble(),
70
              color: Colors.white,
71
              child: IconBar(),
72 73
            ),
          );
74
        },
75 76 77 78 79 80
      ),
      drawer: const GalleryDrawer(),
    );
  }
}

81
class ComplexLayout extends StatefulWidget {
82
  const ComplexLayout({ Key key }) : super(key: key);
83 84

  @override
85
  ComplexLayoutState createState() => ComplexLayoutState();
86

87
  static ComplexLayoutState of(BuildContext context) => context.findAncestorStateOfType<ComplexLayoutState>();
88 89 90 91 92
}

class ComplexLayoutState extends State<ComplexLayout> {
  @override
  Widget build(BuildContext context) {
93 94
    return Scaffold(
      appBar: AppBar(
95
        title: const Text('Advanced Layout'),
96
        actions: <Widget>[
97
          IconButton(
98
            icon: const Icon(Icons.create),
99 100
            tooltip: 'Search',
            onPressed: () {
101
              print('Pressed search');
102
            },
103
          ),
104 105
          TopBarMenu(),
        ],
106
      ),
107
      body: Column(
108
        children: <Widget>[
109 110
          Expanded(
            child: ListView.builder(
111
              key: const Key('complex-scroll'), // this key is used by the driver test
112 113
              itemBuilder: (BuildContext context, int index) {
                if (index % 2 == 0)
114
                  return FancyImageItem(index, key: PageStorageKey<int>(index));
115
                else
116
                  return FancyGalleryItem(index, key: PageStorageKey<int>(index));
117
              },
118
            ),
119
          ),
120
          BottomBar(),
121
        ],
122
      ),
123
      drawer: const GalleryDrawer(),
124 125 126 127 128 129 130
    );
  }
}

class TopBarMenu extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
131
    return PopupMenuButton<String>(
132
      onSelected: (String value) { print('Selected: $value'); },
133
      itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
134
        const PopupMenuItem<String>(
135
          value: 'Friends',
136
          child: MenuItemWithIcon(Icons.people, 'Friends', '5 new'),
137
        ),
138
        const PopupMenuItem<String>(
139
          value: 'Events',
140
          child: MenuItemWithIcon(Icons.event, 'Events', '12 upcoming'),
141
        ),
142
        const PopupMenuItem<String>(
143
          value: 'Events',
144
          child: MenuItemWithIcon(Icons.group, 'Groups', '14'),
145
        ),
146
        const PopupMenuItem<String>(
147
          value: 'Events',
148
          child: MenuItemWithIcon(Icons.image, 'Pictures', '12'),
149
        ),
150
        const PopupMenuItem<String>(
151
          value: 'Events',
152
          child: MenuItemWithIcon(Icons.near_me, 'Nearby', '33'),
153
        ),
154
        const PopupMenuItem<String>(
155
          value: 'Friends',
156
          child: MenuItemWithIcon(Icons.people, 'Friends', '5'),
157
        ),
158
        const PopupMenuItem<String>(
159
          value: 'Events',
160
          child: MenuItemWithIcon(Icons.event, 'Events', '12'),
161
        ),
162
        const PopupMenuItem<String>(
163
          value: 'Events',
164
          child: MenuItemWithIcon(Icons.group, 'Groups', '14'),
165
        ),
166
        const PopupMenuItem<String>(
167
          value: 'Events',
168
          child: MenuItemWithIcon(Icons.image, 'Pictures', '12'),
169
        ),
170
        const PopupMenuItem<String>(
171
          value: 'Events',
172 173 174
          child: MenuItemWithIcon(Icons.near_me, 'Nearby', '33'),
        ),
      ],
175 176 177 178 179
    );
  }
}

class MenuItemWithIcon extends StatelessWidget {
180
  const MenuItemWithIcon(this.icon, this.title, this.subtitle);
181 182 183 184 185 186 187

  final IconData icon;
  final String title;
  final String subtitle;

  @override
  Widget build(BuildContext context) {
188
    return Row(
189
      children: <Widget>[
190 191
        Icon(icon),
        Padding(
192
          padding: const EdgeInsets.only(left: 8.0, right: 8.0),
193
          child: Text(title),
194
        ),
195 196
        Text(subtitle, style: Theme.of(context).textTheme.caption),
      ],
197 198 199 200 201
    );
  }
}

class FancyImageItem extends StatelessWidget {
202
  const FancyImageItem(this.index, {Key key}) : super(key: key);
203 204 205 206 207

  final int index;

  @override
  Widget build(BuildContext context) {
208
    return ListBody(
209
      children: <Widget>[
210 211 212 213
        UserHeader('Ali Connors $index'),
        ItemDescription(),
        ItemImageBox(),
        InfoBar(),
214
        const Padding(
215
          padding: EdgeInsets.symmetric(horizontal: 8.0),
216
          child: Divider(),
217
        ),
218
        IconBar(),
219 220
        FatDivider(),
      ],
221 222 223 224 225
    );
  }
}

class FancyGalleryItem extends StatelessWidget {
226
  const FancyGalleryItem(this.index, {Key key}) : super(key: key);
227 228 229 230

  final int index;
  @override
  Widget build(BuildContext context) {
231
    return ListBody(
232
      children: <Widget>[
233
        const UserHeader('Ali Connors'),
234 235
        ItemGalleryBox(index),
        InfoBar(),
236
        const Padding(
237
          padding: EdgeInsets.symmetric(horizontal: 8.0),
238
          child: Divider(),
239
        ),
240
        IconBar(),
241 242
        FatDivider(),
      ],
243 244 245 246 247 248 249
    );
  }
}

class InfoBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
250
    return Padding(
251
      padding: const EdgeInsets.all(8.0),
252
      child: Row(
253 254
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
255
          const MiniIconWithText(Icons.thumb_up, '42'),
256 257 258
          Text('3 Comments', style: Theme.of(context).textTheme.caption),
        ],
      ),
259 260 261 262 263 264 265
    );
  }
}

class IconBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
266
    return Padding(
267
      padding: const EdgeInsets.only(left: 16.0, right: 16.0),
268
      child: Row(
269
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
270
        children: const <Widget>[
271 272 273
          IconWithText(Icons.thumb_up, 'Like'),
          IconWithText(Icons.comment, 'Comment'),
          IconWithText(Icons.share, 'Share'),
274 275
        ],
      ),
276 277 278 279 280
    );
  }
}

class IconWithText extends StatelessWidget {
281
  const IconWithText(this.icon, this.title);
282 283 284 285 286 287

  final IconData icon;
  final String title;

  @override
  Widget build(BuildContext context) {
288
    return Row(
289
      mainAxisSize: MainAxisSize.min,
290
      children: <Widget>[
291 292
        IconButton(
          icon: Icon(icon),
293
          onPressed: () { print('Pressed $title button'); },
Ian Hickson's avatar
Ian Hickson committed
294
        ),
295 296
        Text(title),
      ],
297 298 299 300 301
    );
  }
}

class MiniIconWithText extends StatelessWidget {
302
  const MiniIconWithText(this.icon, this.title);
303 304 305 306 307 308

  final IconData icon;
  final String title;

  @override
  Widget build(BuildContext context) {
309
    return Row(
310
      mainAxisSize: MainAxisSize.min,
311
      children: <Widget>[
312
        Padding(
313
          padding: const EdgeInsets.only(right: 8.0),
314
          child: Container(
315 316
            width: 16.0,
            height: 16.0,
317
            decoration: BoxDecoration(
318
              color: Theme.of(context).primaryColor,
319
              shape: BoxShape.circle,
320
            ),
321 322
            child: Icon(icon, color: Colors.white, size: 12.0),
          ),
323
        ),
324 325
        Text(title, style: Theme.of(context).textTheme.caption),
      ],
326 327 328 329 330 331 332
    );
  }
}

class FatDivider extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
333
    return Container(
334
      height: 8.0,
335
      color: Theme.of(context).dividerColor,
336 337 338 339 340
    );
  }
}

class UserHeader extends StatelessWidget {
341
  const UserHeader(this.userName);
342 343 344 345 346

  final String userName;

  @override
  Widget build(BuildContext context) {
347
    return Padding(
348
      padding: const EdgeInsets.all(8.0),
349
      child: Row(
350 351
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
352
          const Padding(
353 354 355
            padding: EdgeInsets.only(right: 8.0),
            child: Image(
              image: AssetImage('packages/flutter_gallery_assets/people/square/ali.png'),
356
              width: 32.0,
357 358
              height: 32.0,
            ),
359
          ),
360 361
          Expanded(
            child: Column(
362 363 364
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
365
                RichText(text: TextSpan(
366
                  style: Theme.of(context).textTheme.bodyText2,
367
                  children: <TextSpan>[
368
                    TextSpan(text: userName, style: const TextStyle(fontWeight: FontWeight.bold)),
369
                    const TextSpan(text: ' shared a new '),
370 371
                    const TextSpan(text: 'photo', style: TextStyle(fontWeight: FontWeight.bold)),
                  ],
372
                )),
373
                Row(
374
                  children: <Widget>[
375
                    Text('Yesterday at 11:55 • ', style: Theme.of(context).textTheme.caption),
376 377 378 379 380
                    Icon(Icons.people, size: 16.0, color: Theme.of(context).textTheme.caption.color),
                  ],
                ),
              ],
            ),
381
          ),
382 383 384
          TopBarMenu(),
        ],
      ),
385 386 387 388 389 390 391
    );
  }
}

class ItemDescription extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
392
    return const Padding(
393
      padding: EdgeInsets.all(8.0),
394
      child: Text('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'),
395 396 397 398 399 400 401
    );
  }
}

class ItemImageBox extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
402
    return Padding(
403
      padding: const EdgeInsets.all(8.0),
404 405
      child: Card(
        child: Column(
406 407
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
408
            Stack(
409
              children: <Widget>[
410
                const SizedBox(
411
                  height: 230.0,
412 413
                  child: Image(
                    image: AssetImage('packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png')
414
                  ),
415
                ),
416 417 418
                Theme(
                  data: ThemeData.dark(),
                  child: Row(
419 420
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: <Widget>[
421
                      IconButton(
422
                        icon: const Icon(Icons.edit),
423
                        onPressed: () { print('Pressed edit button'); },
Ian Hickson's avatar
Ian Hickson committed
424
                      ),
425
                      IconButton(
426
                        icon: const Icon(Icons.zoom_in),
427
                        onPressed: () { print('Pressed zoom button'); },
Ian Hickson's avatar
Ian Hickson committed
428
                      ),
429 430
                    ],
                  ),
431
                ),
432
                Positioned(
433 434
                  bottom: 4.0,
                  left: 4.0,
435 436
                  child: Container(
                    decoration: BoxDecoration(
437
                      color: Colors.black54,
438
                      borderRadius: BorderRadius.circular(2.0),
439
                    ),
440
                    padding: const EdgeInsets.all(4.0),
441 442
                    child: RichText(
                      text: const TextSpan(
443 444 445
                        style: TextStyle(color: Colors.white),
                        children: <TextSpan>[
                          TextSpan(
446
                            text: 'Photo by '
447
                          ),
448 449
                          TextSpan(
                            style: TextStyle(fontWeight: FontWeight.bold),
450 451 452 453 454 455 456 457
                            text: 'Chris Godley',
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
458 459
            )
            ,
460
            Padding(
461
              padding: const EdgeInsets.all(8.0),
462
              child: Column(
463 464
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
465 466
                  Text('Artisans of Southern India', style: Theme.of(context).textTheme.bodyText1),
                  Text('Silk Spinners', style: Theme.of(context).textTheme.bodyText2),
467 468 469 470 471 472 473
                  Text('Sivaganga, Tamil Nadu', style: Theme.of(context).textTheme.caption),
                ],
              ),
            ),
          ],
        ),
      ),
474 475 476 477 478
    );
  }
}

class ItemGalleryBox extends StatelessWidget {
479
  const ItemGalleryBox(this.index);
480 481 482 483 484

  final int index;

  @override
  Widget build(BuildContext context) {
485
    final List<String> tabNames = <String>[
486
      'A', 'B', 'C', 'D',
487 488
    ];

489
    return SizedBox(
490
      height: 200.0,
491
      child: DefaultTabController(
Hans Muller's avatar
Hans Muller committed
492
        length: tabNames.length,
493
        child: Column(
494
          children: <Widget>[
495 496
            Expanded(
              child: TabBarView(
497
                children: tabNames.map<Widget>((String tabName) {
498 499 500
                  return Container(
                    key: PageStorageKey<String>(tabName),
                    child: Padding(
501
                      padding: const EdgeInsets.all(8.0),
502 503
                      child: Card(
                        child: Column(
504
                          children: <Widget>[
505 506
                            Expanded(
                              child: Container(
507
                                color: Theme.of(context).primaryColor,
508
                                child: Center(
509
                                  child: Text(tabName, style: Theme.of(context).textTheme.headline5.copyWith(color: Colors.white)),
510 511
                                ),
                              ),
512
                            ),
513
                            Row(
514
                              children: <Widget>[
515
                                IconButton(
516
                                  icon: const Icon(Icons.share),
517
                                  onPressed: () { print('Pressed share'); },
518
                                ),
519
                                IconButton(
520
                                  icon: const Icon(Icons.event),
521
                                  onPressed: () { print('Pressed event'); },
522
                                ),
523 524
                                Expanded(
                                  child: Padding(
525
                                    padding: const EdgeInsets.only(left: 8.0),
526
                                    child: Text('This is item $tabName'),
527 528 529 530 531 532 533 534
                                  ),
                                ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    ),
535
                  );
536 537
                }).toList(),
              ),
538
            ),
539
            Container(
540 541 542 543 544
              child: const TabPageSelector(),
            ),
          ],
        ),
      ),
545 546 547 548 549 550 551
    );
  }
}

class BottomBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
552 553 554 555
    return Container(
      decoration: BoxDecoration(
        border: Border(
          top: BorderSide(
556
            color: Theme.of(context).dividerColor,
557 558 559
            width: 1.0,
          ),
        ),
560
      ),
561
      child: Row(
562
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
563
        children: const <Widget>[
564 565 566 567 568
          BottomBarButton(Icons.new_releases, 'News'),
          BottomBarButton(Icons.people, 'Requests'),
          BottomBarButton(Icons.chat, 'Messenger'),
          BottomBarButton(Icons.bookmark, 'Bookmark'),
          BottomBarButton(Icons.alarm, 'Alarm'),
569 570
        ],
      ),
571 572 573 574 575
    );
  }
}

class BottomBarButton extends StatelessWidget {
576
  const BottomBarButton(this.icon, this.title);
577 578 579 580 581 582

  final IconData icon;
  final String title;

  @override
  Widget build(BuildContext context) {
583
    return Padding(
584
      padding: const EdgeInsets.all(8.0),
585
      child: Column(
586
        children: <Widget>[
587 588
          IconButton(
            icon: Icon(icon),
589
            onPressed: () { print('Pressed: $title'); },
590
          ),
591 592 593
          Text(title, style: Theme.of(context).textTheme.caption),
        ],
      ),
594 595 596 597 598
    );
  }
}

class GalleryDrawer extends StatelessWidget {
599
  const GalleryDrawer({ Key key }) : super(key: key);
600 601 602 603 604

  void _changeTheme(BuildContext context, bool value) {
    ComplexLayoutApp.of(context).lightTheme = value;
  }

605 606 607 608
  void _changeScrollMode(BuildContext context, ScrollMode mode) {
    ComplexLayoutApp.of(context).scrollMode = mode;
  }

609 610
  @override
  Widget build(BuildContext context) {
611
    final ScrollMode currentMode = ComplexLayoutApp.of(context).scrollMode;
612
    return Drawer(
613 614 615
      // Note: for real apps, see the Gallery material Drawer demo. More
      // typically, a drawer would have a fixed header with a scrolling body
      // below it.
616
      child: ListView(
617
        key: const PageStorageKey<String>('gallery-drawer'),
618
        padding: EdgeInsets.zero,
619
        children: <Widget>[
620 621
          FancyDrawerHeader(),
          ListTile(
622
            key: const Key('scroll-switcher'),
623 624 625 626 627
            title: const Text('Scroll Mode'),
            onTap: () {
              _changeScrollMode(context, currentMode == ScrollMode.complex ? ScrollMode.tile : ScrollMode.complex);
             Navigator.pop(context);
            },
628
            trailing: Text(currentMode == ScrollMode.complex ? 'Tile' : 'Complex'),
629
          ),
630
          ListTile(
631
            leading: const Icon(Icons.brightness_5),
632
            title: const Text('Light'),
633
            onTap: () { _changeTheme(context, true); },
634
            selected: ComplexLayoutApp.of(context).lightTheme,
635
            trailing: Radio<bool>(
636 637
              value: true,
              groupValue: ComplexLayoutApp.of(context).lightTheme,
638
              onChanged: (bool value) { _changeTheme(context, value); },
639
            ),
640
          ),
641
          ListTile(
642
            leading: const Icon(Icons.brightness_7),
643
            title: const Text('Dark'),
644
            onTap: () { _changeTheme(context, false); },
645
            selected: !ComplexLayoutApp.of(context).lightTheme,
646
            trailing: Radio<bool>(
647 648 649 650
              value: false,
              groupValue: ComplexLayoutApp.of(context).lightTheme,
              onChanged: (bool value) { _changeTheme(context, value); },
            ),
651
          ),
652
          const Divider(),
653
          ListTile(
654
            leading: const Icon(Icons.hourglass_empty),
655
            title: const Text('Animate Slowly'),
656
            selected: timeDilation != 1.0,
657
            onTap: () { ComplexLayoutApp.of(context).toggleAnimationSpeed(); },
658
            trailing: Checkbox(
659
              value: timeDilation != 1.0,
660
              onChanged: (bool value) { ComplexLayoutApp.of(context).toggleAnimationSpeed(); },
661 662 663 664
            ),
          ),
        ],
      ),
665 666 667 668 669 670 671
    );
  }
}

class FancyDrawerHeader extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
672
    return Container(
673 674
      color: Colors.purple,
      height: 200.0,
675 676
      child: const SafeArea(
        bottom: false,
677
        child: Placeholder(),
678
      ),
679 680 681
    );
  }
}