this repo has no description

refactor: move GalleryPreview widget to its own file and update usage in home_page

+118 -157
+55 -156
lib/screens/home_page.dart
··· 1 1 import 'package:flutter/material.dart'; 2 2 import 'package:grain/api.dart'; 3 3 import 'package:grain/models/gallery.dart'; 4 + import 'package:grain/widgets/gallery_preview.dart'; 4 5 import 'gallery_page.dart'; 5 6 import 'comments_page.dart'; 6 7 import 'profile_page.dart'; ··· 80 81 return const Center(child: Text('No timeline items.')); 81 82 } 82 83 return ListView.separated( 83 - physics: const SlowScrollPhysics(speedFactor: 1.5), 84 84 itemCount: _timeline.length, 85 85 separatorBuilder: (context, index) => 86 86 Divider(color: Colors.grey[200], thickness: 1, height: 1), ··· 182 182 ], 183 183 ), 184 184 const SizedBox(height: 12), 185 - if (gallery.items.isNotEmpty) _GalleryPreview(gallery: gallery), 185 + if (gallery.items.isNotEmpty) GalleryPreview(gallery: gallery), 186 186 if (gallery.title.isNotEmpty) 187 187 Padding( 188 188 padding: const EdgeInsets.only(top: 8), ··· 203 203 ), 204 204 ), 205 205 const SizedBox(height: 8), 206 - Row( 207 - children: [ 208 - IconButton( 209 - icon: Icon( 210 - gallery.viewer != null && gallery.viewer!['fav'] != null 211 - ? Icons.favorite 212 - : Icons.favorite_border, 213 - color: 206 + Padding( 207 + padding: const EdgeInsets.only(top: 8, bottom: 8), 208 + child: Row( 209 + children: [ 210 + GestureDetector( 211 + child: Padding( 212 + padding: const EdgeInsets.only(right: 12), 213 + child: Icon( 214 + size: 18, 214 215 gallery.viewer != null && 215 - gallery.viewer!['fav'] != null 216 - ? Color(0xFFEC4899) 217 - : Colors.black54, 216 + gallery.viewer!['fav'] != null 217 + ? FontAwesomeIcons.solidHeart 218 + : FontAwesomeIcons.heart, 219 + color: 220 + gallery.viewer != null && 221 + gallery.viewer!['fav'] != null 222 + ? Color(0xFFEC4899) 223 + : Colors.black54, 224 + ), 225 + ), 226 + onTap: () {}, 218 227 ), 219 - onPressed: () {}, 220 - ), 221 - if (gallery.favCount != null) 222 - Padding( 223 - padding: const EdgeInsets.only(right: 12), 224 - child: Text( 225 - gallery.favCount.toString(), 226 - style: const TextStyle( 227 - fontSize: 14, 228 + if (gallery.favCount != null) 229 + Padding( 230 + padding: const EdgeInsets.only(right: 12), 231 + child: Text( 232 + gallery.favCount.toString(), 233 + style: const TextStyle( 234 + fontSize: 14, 235 + color: Colors.black54, 236 + ), 237 + ), 238 + ), 239 + GestureDetector( 240 + onTap: () { 241 + Navigator.of(context).push( 242 + MaterialPageRoute( 243 + builder: (context) => 244 + CommentsPage(galleryUri: gallery.uri), 245 + ), 246 + ); 247 + }, 248 + child: Padding( 249 + padding: const EdgeInsets.only(left: 12, right: 12), 250 + child: Icon( 251 + FontAwesomeIcons.comment, 252 + size: 18, 228 253 color: Colors.black54, 229 254 ), 230 255 ), 231 256 ), 232 - IconButton( 233 - icon: const Icon(Icons.comment_outlined), 234 - onPressed: () { 235 - Navigator.of(context).push( 236 - MaterialPageRoute( 237 - builder: (context) => 238 - CommentsPage(galleryUri: gallery.uri), 257 + if (gallery.commentCount != null) 258 + Text( 259 + gallery.commentCount.toString(), 260 + style: const TextStyle( 261 + fontSize: 14, 262 + color: Colors.black54, 239 263 ), 240 - ); 241 - }, 242 - ), 243 - if (gallery.commentCount != null) 244 - Text( 245 - gallery.commentCount.toString(), 246 - style: const TextStyle( 247 - fontSize: 14, 248 - color: Colors.black54, 249 264 ), 250 - ), 251 - ], 265 + ], 266 + ), 252 267 ), 253 268 ], 254 269 ), ··· 573 588 ); 574 589 } 575 590 } 576 - 577 - class _GalleryPreview extends StatelessWidget { 578 - final Gallery gallery; 579 - 580 - const _GalleryPreview({required this.gallery}); 581 - 582 - @override 583 - Widget build(BuildContext context) { 584 - final photos = gallery.items 585 - .where((item) => item.thumb.isNotEmpty) 586 - .toList(); 587 - return AspectRatio( 588 - aspectRatio: 3 / 2, 589 - child: Row( 590 - children: [ 591 - Expanded( 592 - flex: 2, 593 - child: photos.isNotEmpty 594 - ? Image.network( 595 - photos[0].thumb, 596 - fit: BoxFit.cover, 597 - width: double.infinity, 598 - height: double.infinity, 599 - ) 600 - : Container(color: Colors.grey[300]), 601 - ), 602 - const SizedBox(width: 2), 603 - Expanded( 604 - flex: 1, 605 - child: Column( 606 - children: [ 607 - Expanded( 608 - child: photos.length > 1 609 - ? Image.network( 610 - photos[1].thumb, 611 - fit: BoxFit.cover, 612 - width: double.infinity, 613 - height: double.infinity, 614 - ) 615 - : Container(color: Colors.grey[200]), 616 - ), 617 - const SizedBox(height: 2), 618 - Expanded( 619 - child: photos.length > 2 620 - ? Image.network( 621 - photos[2].thumb, 622 - fit: BoxFit.cover, 623 - width: double.infinity, 624 - height: double.infinity, 625 - ) 626 - : Container(color: Colors.grey[200]), 627 - ), 628 - ], 629 - ), 630 - ), 631 - ], 632 - ), 633 - ); 634 - } 635 - } 636 - 637 - class GalleryThumbnail extends StatelessWidget { 638 - final Gallery gallery; 639 - 640 - const GalleryThumbnail({super.key, required this.gallery}); 641 - 642 - @override 643 - Widget build(BuildContext context) { 644 - if (gallery.items.isEmpty) { 645 - return Container(color: Colors.grey[300]); 646 - } 647 - final firstItem = gallery.items[0]; 648 - return ClipRRect( 649 - borderRadius: BorderRadius.circular(12), 650 - child: Image.network( 651 - firstItem.thumb, 652 - fit: BoxFit.cover, 653 - loadingBuilder: (context, child, loadingProgress) { 654 - if (loadingProgress == null) return child; 655 - return Center( 656 - child: CircularProgressIndicator( 657 - value: loadingProgress.expectedTotalBytes != null 658 - ? loadingProgress.cumulativeBytesLoaded / 659 - (loadingProgress.expectedTotalBytes ?? 1) 660 - : null, 661 - ), 662 - ); 663 - }, 664 - errorBuilder: (context, error, stackTrace) { 665 - return Container( 666 - color: Colors.grey[300], 667 - child: const Icon(Icons.broken_image, color: Colors.grey), 668 - ); 669 - }, 670 - ), 671 - ); 672 - } 673 - } 674 - 675 - class SlowScrollPhysics extends ScrollPhysics { 676 - final double speedFactor; 677 - const SlowScrollPhysics({this.speedFactor = 1.5, super.parent}); 678 - 679 - @override 680 - SlowScrollPhysics applyTo(ScrollPhysics? ancestor) { 681 - return SlowScrollPhysics( 682 - speedFactor: speedFactor, 683 - parent: buildParent(ancestor), 684 - ); 685 - } 686 - 687 - @override 688 - double applyPhysicsToUserOffset(ScrollMetrics position, double offset) { 689 - return super.applyPhysicsToUserOffset(position, offset / speedFactor); 690 - } 691 - }
+62
lib/widgets/gallery_preview.dart
··· 1 + import 'package:flutter/material.dart'; 2 + import 'package:grain/models/gallery.dart'; 3 + 4 + class GalleryPreview extends StatelessWidget { 5 + final Gallery gallery; 6 + 7 + const GalleryPreview({super.key, required this.gallery}); 8 + 9 + @override 10 + Widget build(BuildContext context) { 11 + final photos = gallery.items 12 + .where((item) => item.thumb.isNotEmpty) 13 + .toList(); 14 + return AspectRatio( 15 + aspectRatio: 3 / 2, 16 + child: Row( 17 + children: [ 18 + Expanded( 19 + flex: 2, 20 + child: photos.isNotEmpty 21 + ? Image.network( 22 + photos[0].thumb, 23 + fit: BoxFit.cover, 24 + width: double.infinity, 25 + height: double.infinity, 26 + ) 27 + : Container(color: Colors.grey[300]), 28 + ), 29 + const SizedBox(width: 2), 30 + Expanded( 31 + flex: 1, 32 + child: Column( 33 + children: [ 34 + Expanded( 35 + child: photos.length > 1 36 + ? Image.network( 37 + photos[1].thumb, 38 + fit: BoxFit.cover, 39 + width: double.infinity, 40 + height: double.infinity, 41 + ) 42 + : Container(color: Colors.grey[200]), 43 + ), 44 + const SizedBox(height: 2), 45 + Expanded( 46 + child: photos.length > 2 47 + ? Image.network( 48 + photos[2].thumb, 49 + fit: BoxFit.cover, 50 + width: double.infinity, 51 + height: double.infinity, 52 + ) 53 + : Container(color: Colors.grey[200]), 54 + ), 55 + ], 56 + ), 57 + ), 58 + ], 59 + ), 60 + ); 61 + } 62 + }
+1 -1
pubspec.yaml
··· 16 16 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 17 # In Windows, build-name is used as the major, minor, and patch parts 18 18 # of the product and file versions while build-number is used as the build suffix. 19 - version: 1.0.0+6 19 + version: 1.0.0+8 20 20 21 21 environment: 22 22 sdk: ^3.8.1