| 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 | class PictureCachePage extends StatelessWidget { |
| 8 | const PictureCachePage({super.key}); |
| 9 | |
| 10 | static const List<String> kTabNames = <String>['1' , '2' , '3' , '4' , '5' ]; |
| 11 | |
| 12 | @override |
| 13 | Widget build(BuildContext context) { |
| 14 | return DefaultTabController( |
| 15 | length: kTabNames.length, // This is the number of tabs. |
| 16 | child: Scaffold( |
| 17 | appBar: AppBar( |
| 18 | title: const Text('Picture Cache' ), |
| 19 | // pinned: true, |
| 20 | // expandedHeight: 50.0, |
| 21 | // forceElevated: innerBoxIsScrolled, |
| 22 | bottom: TabBar(tabs: kTabNames.map((String name) => Tab(text: name)).toList()), |
| 23 | ), |
| 24 | body: TabBarView( |
| 25 | key: const Key('tabbar_view' ), // this key is used by the driver test |
| 26 | children: kTabNames.map((String name) { |
| 27 | return SafeArea( |
| 28 | top: false, |
| 29 | bottom: false, |
| 30 | child: Builder( |
| 31 | builder: (BuildContext context) { |
| 32 | return ListView.builder( |
| 33 | itemBuilder: (BuildContext context, int index) => ListItem(index: index), |
| 34 | ); |
| 35 | }, |
| 36 | ), |
| 37 | ); |
| 38 | }).toList(), |
| 39 | ), |
| 40 | ), |
| 41 | ); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | class ListItem extends StatelessWidget { |
| 46 | const ListItem({super.key, required this.index}); |
| 47 | |
| 48 | final int index; |
| 49 | |
| 50 | static const String kMockChineseTitle = '复杂的中文标题?复杂的中文标题!' ; |
| 51 | static const String kMockName = '李耳123456' ; |
| 52 | static const int kMockCount = 999; |
| 53 | |
| 54 | @override |
| 55 | Widget build(BuildContext context) { |
| 56 | final List<Widget> contents = <Widget>[ |
| 57 | const SizedBox(height: 15), |
| 58 | _buildUserInfo(), |
| 59 | const SizedBox(height: 10), |
| 60 | ]; |
| 61 | if (index % 3 != 0) { |
| 62 | contents.add(_buildImageContent()); |
| 63 | } else { |
| 64 | contents.addAll(<Widget>[ |
| 65 | Padding(padding: const EdgeInsets.only(left: 40, right: 15), child: _buildContentText()), |
| 66 | const SizedBox(height: 10), |
| 67 | Padding(padding: const EdgeInsets.only(left: 40, right: 15), child: _buildBottomRow()), |
| 68 | ]); |
| 69 | } |
| 70 | contents.addAll(<Widget>[ |
| 71 | const SizedBox(height: 13), |
| 72 | buildDivider(0.5, const EdgeInsets.only(left: 40, right: 15)), |
| 73 | ]); |
| 74 | return MaterialButton( |
| 75 | onPressed: () {}, |
| 76 | padding: EdgeInsets.zero, |
| 77 | child: Column( |
| 78 | mainAxisSize: MainAxisSize.min, |
| 79 | crossAxisAlignment: CrossAxisAlignment.start, |
| 80 | children: contents, |
| 81 | ), |
| 82 | ); |
| 83 | } |
| 84 | |
| 85 | Text _buildRankText() { |
| 86 | return Text( |
| 87 | (index + 1).toString(), |
| 88 | style: TextStyle( |
| 89 | fontSize: 15, |
| 90 | color: index + 1 <= 3 ? const Color(0xFFE5645F) : Colors.black, |
| 91 | fontWeight: FontWeight.bold, |
| 92 | ), |
| 93 | ); |
| 94 | } |
| 95 | |
| 96 | Widget _buildImageContent() { |
| 97 | return Row( |
| 98 | children: <Widget>[ |
| 99 | const SizedBox(width: 40), |
| 100 | Expanded( |
| 101 | child: Column( |
| 102 | mainAxisSize: MainAxisSize.min, |
| 103 | crossAxisAlignment: CrossAxisAlignment.start, |
| 104 | children: <Widget>[ |
| 105 | Padding(padding: const EdgeInsets.only(right: 30), child: _buildContentText()), |
| 106 | const SizedBox(height: 10), |
| 107 | _buildBottomRow(), |
| 108 | ], |
| 109 | ), |
| 110 | ), |
| 111 | Image.asset( |
| 112 | index.isEven ? 'food/butternut_squash_soup.png' : 'food/cherry_pie.png' , |
| 113 | package: 'flutter_gallery_assets' , |
| 114 | fit: BoxFit.cover, |
| 115 | width: 110, |
| 116 | height: 70, |
| 117 | ), |
| 118 | const SizedBox(width: 15), |
| 119 | ], |
| 120 | ); |
| 121 | } |
| 122 | |
| 123 | Widget _buildContentText() { |
| 124 | return const Text( |
| 125 | kMockChineseTitle, |
| 126 | style: TextStyle(fontSize: 16), |
| 127 | maxLines: 2, |
| 128 | overflow: TextOverflow.ellipsis, |
| 129 | ); |
| 130 | } |
| 131 | |
| 132 | Widget _buildBottomRow() { |
| 133 | return Row( |
| 134 | children: <Widget>[ |
| 135 | Container( |
| 136 | padding: const EdgeInsets.symmetric(horizontal: 7), |
| 137 | height: 16, |
| 138 | alignment: Alignment.center, |
| 139 | decoration: BoxDecoration( |
| 140 | borderRadius: BorderRadius.circular(8), |
| 141 | color: const Color(0xFFFBEEEE), |
| 142 | ), |
| 143 | child: Row( |
| 144 | children: <Widget>[ |
| 145 | const SizedBox(width: 3), |
| 146 | Text( |
| 147 | 'hot: ${_convertCountToStr(kMockCount)}' , |
| 148 | style: const TextStyle(color: Color(0xFFE5645F), fontSize: 11), |
| 149 | ), |
| 150 | ], |
| 151 | ), |
| 152 | ), |
| 153 | const SizedBox(width: 9), |
| 154 | const Text('ans: $kMockCount' , style: TextStyle(color: Color(0xFF999999), fontSize: 11)), |
| 155 | const SizedBox(width: 9), |
| 156 | const Text('like: $kMockCount' , style: TextStyle(color: Color(0xFF999999), fontSize: 11)), |
| 157 | ], |
| 158 | ); |
| 159 | } |
| 160 | |
| 161 | String _convertCountToStr(int count) { |
| 162 | return switch (count) { |
| 163 | < 10000 => count.toString(), |
| 164 | < 100000 => ' ${(count / 10000).toStringAsPrecision(2)}w' , |
| 165 | _ => ' ${(count / 10000).floor()}w' , |
| 166 | }; |
| 167 | } |
| 168 | |
| 169 | Widget _buildUserInfo() { |
| 170 | return GestureDetector( |
| 171 | onTap: () {}, |
| 172 | child: Row( |
| 173 | children: <Widget>[ |
| 174 | Container(width: 40, alignment: Alignment.center, child: _buildRankText()), |
| 175 | const CircleAvatar(radius: 11.5), |
| 176 | const SizedBox(width: 6), |
| 177 | ConstrainedBox( |
| 178 | constraints: const BoxConstraints(maxWidth: 250), |
| 179 | child: const Text( |
| 180 | kMockName, |
| 181 | maxLines: 1, |
| 182 | overflow: TextOverflow.ellipsis, |
| 183 | style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), |
| 184 | ), |
| 185 | ), |
| 186 | const SizedBox(width: 4), |
| 187 | const SizedBox(width: 15), |
| 188 | ], |
| 189 | ), |
| 190 | ); |
| 191 | } |
| 192 | |
| 193 | Widget buildDivider(double height, EdgeInsets padding) { |
| 194 | return Container(padding: padding, height: height, color: const Color(0xFFF5F5F5)); |
| 195 | } |
| 196 | } |
| 197 | |