main.dart 20.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
// Copyright 2015 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.

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

void main() {
  runApp(
    new ComplexLayoutApp()
  );
}

14 15
enum ScrollMode { complex, tile }

16 17 18 19 20 21 22 23 24 25 26 27 28
class ComplexLayoutApp extends StatefulWidget {
  @override
  ComplexLayoutAppState createState() => new ComplexLayoutAppState();

  static ComplexLayoutAppState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<ComplexLayoutAppState>());
}

class ComplexLayoutAppState extends State<ComplexLayoutApp> {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      theme: lightTheme ? new ThemeData.light() : new ThemeData.dark(),
      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 60 61 62 63 64 65 66 67 68
class TileScrollLayout extends StatelessWidget {
  const TileScrollLayout({ Key key }) : super(key: key);

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

81
class ComplexLayout extends StatefulWidget {
82
  const ComplexLayout({ Key key }) : super(key: key);
83 84 85 86 87 88 89 90 91 92 93 94

  @override
  ComplexLayoutState createState() => new ComplexLayoutState();

  static ComplexLayoutState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<ComplexLayoutState>());
}

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

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

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

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

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 new ListBody(
209
      children: <Widget>[
210
        new UserHeader('Ali Connors $index'),
211 212 213
        new ItemDescription(),
        new ItemImageBox(),
        new InfoBar(),
214
        const Padding(
215
          padding: const EdgeInsets.symmetric(horizontal: 8.0),
216
          child: const Divider()
217 218 219 220 221 222 223 224 225
        ),
        new IconBar(),
        new FatDivider()
      ]
    );
  }
}

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 new ListBody(
232
      children: <Widget>[
233
        const UserHeader('Ali Connors'),
234 235
        new ItemGalleryBox(index),
        new InfoBar(),
236
        const Padding(
237
          padding: const EdgeInsets.symmetric(horizontal: 8.0),
238
          child: const Divider()
239 240 241 242 243 244 245 246 247 248 249 250
        ),
        new IconBar(),
        new FatDivider()
      ]
    );
  }
}

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

class IconBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Padding(
267
      padding: const EdgeInsets.only(left: 16.0, right: 16.0),
268 269 270
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
271
          const IconWithText(Icons.thumb_up, 'Like'),
272
          const IconWithText(Icons.comment, 'Comment'),
273
          const 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 288

  final IconData icon;
  final String title;

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

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

  final IconData icon;
  final String title;

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

class FatDivider extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      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 347

  final String userName;

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

class ItemDescription extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
392
    return const Padding(
393
      padding: const EdgeInsets.all(8.0),
394
      child: const 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 402
    );
  }
}

class ItemImageBox extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Padding(
403
      padding: const EdgeInsets.all(8.0),
404 405 406 407 408 409
      child: new Card(
        child: new Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            new Stack(
              children: <Widget>[
410
                const SizedBox(
411
                  height: 230.0,
412 413
                  child: const Image(
                    image: const AssetImage('packages/flutter_gallery_assets/top_10_australian_beaches.png')
414 415 416 417 418 419 420
                  )
                ),
                new Theme(
                  data: new ThemeData.dark(),
                  child: new Row(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: <Widget>[
Ian Hickson's avatar
Ian Hickson committed
421
                      new IconButton(
422
                        icon: const Icon(Icons.edit),
Ian Hickson's avatar
Ian Hickson committed
423 424 425
                        onPressed: () { print('Pressed edit button'); }
                      ),
                      new IconButton(
426
                        icon: const Icon(Icons.zoom_in),
Ian Hickson's avatar
Ian Hickson committed
427 428
                        onPressed: () { print('Pressed zoom button'); }
                      ),
429 430 431 432 433 434 435 436
                    ]
                  )
                ),
                new Positioned(
                  bottom: 4.0,
                  left: 4.0,
                  child: new Container(
                    decoration: new BoxDecoration(
437
                      color: Colors.black54,
438
                      borderRadius: new BorderRadius.circular(2.0)
439
                    ),
440
                    padding: const EdgeInsets.all(4.0),
441 442
                    child: new RichText(
                      text: new TextSpan(
443
                        style: const TextStyle(color: Colors.white),
444
                        children: <TextSpan>[
445
                          const TextSpan(
446
                            text: 'Photo by '
447
                          ),
448 449
                          const TextSpan(
                            style: const TextStyle(fontWeight: FontWeight.bold),
450
                            text: 'Magic Mike'
451 452 453 454 455 456 457 458 459 460
                          )
                        ]
                      )
                    )
                  )
                )
              ]
            )
            ,
            new Padding(
461
              padding: const EdgeInsets.all(8.0),
462 463 464
              child: new Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
465 466 467
                  new Text('Where can you find that amazing sunset?', style: Theme.of(context).textTheme.body2),
                  new Text('The sun sets over stinson beach', style: Theme.of(context).textTheme.body1),
                  new Text('flutter.io/amazingsunsets', style: Theme.of(context).textTheme.caption)
468 469 470 471 472 473 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 490
    ];

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

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

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

  final IconData icon;
  final String title;

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

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 new Drawer(
613
      child: new ListView(
614 615
        children: <Widget>[
          new FancyDrawerHeader(),
616 617 618 619 620
          new ListTile(
            key: const Key('scroll-switcher'),
            onTap: () { _changeScrollMode(context, currentMode == ScrollMode.complex ? ScrollMode.tile : ScrollMode.complex); },
            trailing: new Text(currentMode == ScrollMode.complex ? 'Tile' : 'Complex')
          ),
621
          new ListTile(
622
            leading: const Icon(Icons.brightness_5),
623
            title: const Text('Light'),
624
            onTap: () { _changeTheme(context, true); },
625
            selected: ComplexLayoutApp.of(context).lightTheme,
626 627 628 629 630
            trailing: new Radio<bool>(
              value: true,
              groupValue: ComplexLayoutApp.of(context).lightTheme,
              onChanged: (bool value) { _changeTheme(context, value); }
            ),
631
          ),
632
          new ListTile(
633
            leading: const Icon(Icons.brightness_7),
634
            title: const Text('Dark'),
635
            onTap: () { _changeTheme(context, false); },
636
            selected: !ComplexLayoutApp.of(context).lightTheme,
637 638 639 640 641
            trailing: new Radio<bool>(
              value: false,
              groupValue: ComplexLayoutApp.of(context).lightTheme,
              onChanged: (bool value) { _changeTheme(context, value); },
            ),
642
          ),
643
          const Divider(),
644
          new ListTile(
645
            leading: const Icon(Icons.hourglass_empty),
646
            title: const Text('Animate Slowly'),
647
            selected: timeDilation != 1.0,
648 649 650 651 652 653 654 655
            onTap: () { ComplexLayoutApp.of(context).toggleAnimationSpeed(); },
            trailing: new Checkbox(
              value: timeDilation != 1.0,
              onChanged: (bool value) { ComplexLayoutApp.of(context).toggleAnimationSpeed(); }
            ),
          ),
        ],
      ),
656 657 658 659 660 661 662 663
    );
  }
}

class FancyDrawerHeader extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
664 665
      color: Colors.purple,
      height: 200.0,
666 667 668
    );
  }
}