widgets.dart 4.07 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 10 11 12
// 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 'sections.dart';

const double kSectionIndicatorWidth = 32.0;

// The card for a single section. Displays the section's gradient and background image.
class SectionCard extends StatelessWidget {
13
  const SectionCard({ super.key, required this.section });
14 15 16 17 18

  final Section section;

  @override
  Widget build(BuildContext context) {
19
    return Semantics(
20 21
      label: section.title,
      button: true,
22 23 24
      child: DecoratedBox(
        decoration: BoxDecoration(
          gradient: LinearGradient(
25
            colors: <Color>[
26 27
              section.leftColor!,
              section.rightColor!,
28 29 30
            ],
          ),
        ),
31
        child: Image.asset(
32
          section.backgroundAsset!,
33 34 35 36
          package: section.backgroundAssetPackage,
          color: const Color.fromRGBO(255, 255, 255, 0.075),
          colorBlendMode: BlendMode.modulate,
          fit: BoxFit.cover,
37
        ),
38
      ),
39 40 41 42 43 44 45
    );
  }
}

// The title is rendered with two overlapping text widgets that are vertically
// offset a little. It's supposed to look sort-of 3D.
class SectionTitle extends StatelessWidget {
46
  const SectionTitle({
47
    super.key,
48 49 50
    required this.section,
    required this.scale,
    required this.opacity,
51
  }) : assert(opacity >= 0.0 && opacity <= 1.0);
52 53 54 55 56

  final Section section;
  final double scale;
  final double opacity;

57 58 59 60 61 62 63 64 65 66 67 68 69
  static const TextStyle sectionTitleStyle = TextStyle(
    fontFamily: 'Raleway',
    inherit: false,
    fontSize: 24.0,
    fontWeight: FontWeight.w500,
    color: Colors.white,
    textBaseline: TextBaseline.alphabetic,
  );

  static final TextStyle sectionTitleShadowStyle = sectionTitleStyle.copyWith(
    color: const Color(0x19000000),
  );

70 71
  @override
  Widget build(BuildContext context) {
72 73
    return IgnorePointer(
      child: Opacity(
74
        opacity: opacity,
75 76
        child: Transform(
          transform: Matrix4.identity()..scale(scale),
77
          alignment: Alignment.center,
78
          child: Stack(
79
            children: <Widget>[
80
              Positioned(
81
                top: 4.0,
82
                child: Text(section.title!, style: sectionTitleShadowStyle),
83
              ),
84
              Text(section.title!, style: sectionTitleStyle),
85 86 87 88 89 90 91 92 93 94
            ],
          ),
        ),
      ),
    );
  }
}

// Small horizontal bar that indicates the selected section.
class SectionIndicator extends StatelessWidget {
95
  const SectionIndicator({ super.key, this.opacity = 1.0 });
96 97 98 99 100

  final double opacity;

  @override
  Widget build(BuildContext context) {
101 102
    return IgnorePointer(
      child: Container(
103 104
        width: kSectionIndicatorWidth,
        height: 3.0,
105
        color: Colors.white.withOpacity(opacity),
106 107 108 109 110 111 112
      ),
    );
  }
}

// Display a single SectionDetail.
class SectionDetailView extends StatelessWidget {
113
  SectionDetailView({ super.key, required this.detail })
114
    : assert(detail.imageAsset != null),
115
      assert((detail.imageAsset ?? detail.title) != null);
116 117 118 119 120

  final SectionDetail detail;

  @override
  Widget build(BuildContext context) {
121 122 123 124 125
    final Widget image = DecoratedBox(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(6.0),
        image: DecorationImage(
          image: AssetImage(
126
            detail.imageAsset!,
127 128
            package: detail.imageAssetPackage,
          ),
129
          fit: BoxFit.cover,
130 131 132 133 134 135
        ),
      ),
    );

    Widget item;
    if (detail.title == null && detail.subtitle == null) {
136
      item = Container(
137 138
        height: 240.0,
        padding: const EdgeInsets.all(16.0),
139
        child: SafeArea(
140 141 142 143
          top: false,
          bottom: false,
          child: image,
        ),
144 145
      );
    } else {
146
      item = ListTile(
147 148
        title: Text(detail.title!),
        subtitle: Text(detail.subtitle!),
149
        leading: SizedBox(width: 32.0, height: 32.0, child: image),
150 151 152
      );
    }

153 154
    return DecoratedBox(
      decoration: BoxDecoration(color: Colors.grey.shade200),
155 156 157 158
      child: item,
    );
  }
}