1 | // Copyright 2014 The Flutter 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 | |
5 | import 'package:flutter/material.dart'; |
6 | |
7 | /// Flutter code sample for [CarouselView]. |
8 | |
9 | void main() => runApp(const CarouselExampleApp()); |
10 | |
11 | class CarouselExampleApp extends StatelessWidget { |
12 | const CarouselExampleApp({super.key}); |
13 | |
14 | @override |
15 | Widget build(BuildContext context) { |
16 | return MaterialApp( |
17 | debugShowCheckedModeBanner: false, |
18 | home: Scaffold( |
19 | appBar: AppBar( |
20 | leading: const Icon(Icons.cast), |
21 | title: const Text('Flutter TV' ), |
22 | actions: const <Widget>[ |
23 | Padding( |
24 | padding: EdgeInsetsDirectional.only(end: 16.0), |
25 | child: CircleAvatar(child: Icon(Icons.account_circle)), |
26 | ), |
27 | ], |
28 | ), |
29 | body: const CarouselExample(), |
30 | ), |
31 | ); |
32 | } |
33 | } |
34 | |
35 | class CarouselExample extends StatefulWidget { |
36 | const CarouselExample({super.key}); |
37 | |
38 | @override |
39 | State<CarouselExample> createState() => _CarouselExampleState(); |
40 | } |
41 | |
42 | class _CarouselExampleState extends State<CarouselExample> { |
43 | final CarouselController controller = CarouselController(initialItem: 1); |
44 | |
45 | @override |
46 | void dispose() { |
47 | controller.dispose(); |
48 | super.dispose(); |
49 | } |
50 | |
51 | @override |
52 | Widget build(BuildContext context) { |
53 | final double height = MediaQuery.sizeOf(context).height; |
54 | |
55 | return ListView( |
56 | children: <Widget>[ |
57 | ConstrainedBox( |
58 | constraints: BoxConstraints(maxHeight: height / 2), |
59 | child: CarouselView.weighted( |
60 | controller: controller, |
61 | itemSnapping: true, |
62 | flexWeights: const <int>[1, 7, 1], |
63 | children: |
64 | ImageInfo.values.map((ImageInfo image) { |
65 | return HeroLayoutCard(imageInfo: image); |
66 | }).toList(), |
67 | ), |
68 | ), |
69 | const SizedBox(height: 20), |
70 | const Padding( |
71 | padding: EdgeInsetsDirectional.only(top: 8.0, start: 8.0), |
72 | child: Text('Multi-browse layout' ), |
73 | ), |
74 | ConstrainedBox( |
75 | constraints: const BoxConstraints(maxHeight: 50), |
76 | child: CarouselView.weighted( |
77 | flexWeights: const <int>[1, 2, 3, 2, 1], |
78 | consumeMaxWeight: false, |
79 | children: List<Widget>.generate(20, (int index) { |
80 | return ColoredBox( |
81 | color: Colors.primaries[index % Colors.primaries.length].withOpacity(0.8), |
82 | child: const SizedBox.expand(), |
83 | ); |
84 | }), |
85 | ), |
86 | ), |
87 | const SizedBox(height: 20), |
88 | ConstrainedBox( |
89 | constraints: const BoxConstraints(maxHeight: 200), |
90 | child: CarouselView.weighted( |
91 | flexWeights: const <int>[3, 3, 3, 2, 1], |
92 | consumeMaxWeight: false, |
93 | children: |
94 | CardInfo.values.map((CardInfo info) { |
95 | return ColoredBox( |
96 | color: info.backgroundColor, |
97 | child: Center( |
98 | child: Column( |
99 | mainAxisAlignment: MainAxisAlignment.center, |
100 | children: <Widget>[ |
101 | Icon(info.icon, color: info.color, size: 32.0), |
102 | Text( |
103 | info.label, |
104 | style: const TextStyle(fontWeight: FontWeight.bold), |
105 | overflow: TextOverflow.clip, |
106 | softWrap: false, |
107 | ), |
108 | ], |
109 | ), |
110 | ), |
111 | ); |
112 | }).toList(), |
113 | ), |
114 | ), |
115 | const SizedBox(height: 20), |
116 | const Padding( |
117 | padding: EdgeInsetsDirectional.only(top: 8.0, start: 8.0), |
118 | child: Text('Uncontained layout' ), |
119 | ), |
120 | ConstrainedBox( |
121 | constraints: const BoxConstraints(maxHeight: 200), |
122 | child: CarouselView( |
123 | itemExtent: 330, |
124 | shrinkExtent: 200, |
125 | children: List<Widget>.generate(20, (int index) { |
126 | return UncontainedLayoutCard(index: index, label: 'Show $index' ); |
127 | }), |
128 | ), |
129 | ), |
130 | ], |
131 | ); |
132 | } |
133 | } |
134 | |
135 | class HeroLayoutCard extends StatelessWidget { |
136 | const HeroLayoutCard({super.key, required this.imageInfo}); |
137 | |
138 | final ImageInfo imageInfo; |
139 | |
140 | @override |
141 | Widget build(BuildContext context) { |
142 | final double width = MediaQuery.sizeOf(context).width; |
143 | return Stack( |
144 | alignment: AlignmentDirectional.bottomStart, |
145 | children: <Widget>[ |
146 | ClipRect( |
147 | child: OverflowBox( |
148 | maxWidth: width * 7 / 8, |
149 | minWidth: width * 7 / 8, |
150 | child: Image( |
151 | fit: BoxFit.cover, |
152 | image: NetworkImage( |
153 | 'https://flutter.github.io/assets-for-api-docs/assets/material/${imageInfo.url}', |
154 | ), |
155 | ), |
156 | ), |
157 | ), |
158 | Padding( |
159 | padding: const EdgeInsets.all(18.0), |
160 | child: Column( |
161 | crossAxisAlignment: CrossAxisAlignment.start, |
162 | mainAxisSize: MainAxisSize.min, |
163 | children: <Widget>[ |
164 | Text( |
165 | imageInfo.title, |
166 | overflow: TextOverflow.clip, |
167 | softWrap: false, |
168 | style: Theme.of(context).textTheme.headlineLarge?.copyWith(color: Colors.white), |
169 | ), |
170 | const SizedBox(height: 10), |
171 | Text( |
172 | imageInfo.subtitle, |
173 | overflow: TextOverflow.clip, |
174 | softWrap: false, |
175 | style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.white), |
176 | ), |
177 | ], |
178 | ), |
179 | ), |
180 | ], |
181 | ); |
182 | } |
183 | } |
184 | |
185 | class UncontainedLayoutCard extends StatelessWidget { |
186 | const UncontainedLayoutCard({super.key, required this.index, required this.label}); |
187 | |
188 | final int index; |
189 | final String label; |
190 | |
191 | @override |
192 | Widget build(BuildContext context) { |
193 | return ColoredBox( |
194 | color: Colors.primaries[index % Colors.primaries.length].withOpacity(0.5), |
195 | child: Center( |
196 | child: Text( |
197 | label, |
198 | style: const TextStyle(color: Colors.white, fontSize: 20), |
199 | overflow: TextOverflow.clip, |
200 | softWrap: false, |
201 | ), |
202 | ), |
203 | ); |
204 | } |
205 | } |
206 | |
207 | enum CardInfo { |
208 | camera('Cameras', Icons.video_call, Color(0xff2354C7), Color(0xffECEFFD)), |
209 | lighting('Lighting', Icons.lightbulb, Color(0xff806C2A), Color(0xffFAEEDF)), |
210 | climate('Climate', Icons.thermostat, Color(0xffA44D2A), Color(0xffFAEDE7)), |
211 | wifi('Wifi', Icons.wifi, Color(0xff417345), Color(0xffE5F4E0)), |
212 | media('Media', Icons.library_music, Color(0xff2556C8), Color(0xffECEFFD)), |
213 | security('Security', Icons.crisis_alert, Color(0xff794C01), Color(0xffFAEEDF)), |
214 | safety('Safety', Icons.medical_services, Color(0xff2251C5), Color(0xffECEFFD)), |
215 | more('', Icons.add, Color(0xff201D1C), Color(0xffE3DFD8)); |
216 | |
217 | const CardInfo(this.label, this.icon, this.color, this.backgroundColor); |
218 | final String label; |
219 | final IconData icon; |
220 | final Color color; |
221 | final Color backgroundColor; |
222 | } |
223 | |
224 | enum ImageInfo { |
225 | image0('The Flow', 'Sponsored | Season 1 Now Streaming', 'content_based_color_scheme_1.png'), |
226 | image1( |
227 | 'Through the Pane', |
228 | 'Sponsored | Season 1 Now Streaming', |
229 | 'content_based_color_scheme_2.png', |
230 | ), |
231 | image2('Iridescence', 'Sponsored | Season 1 Now Streaming', 'content_based_color_scheme_3.png'), |
232 | image3('Sea Change', 'Sponsored | Season 1 Now Streaming', 'content_based_color_scheme_4.png'), |
233 | image4('Blue Symphony', 'Sponsored | Season 1 Now Streaming', 'content_based_color_scheme_5.png'), |
234 | image5('When It Rains', 'Sponsored | Season 1 Now Streaming', 'content_based_color_scheme_6.png'); |
235 | |
236 | const ImageInfo(this.title, this.subtitle, this.url); |
237 | final String title; |
238 | final String subtitle; |
239 | final String url; |
240 | } |
241 | |