Skip to content

Commit 81ca47e

Browse files
authored
Bugfix/product image (#7)
* Update ProductImageDialog * Change catalog screen button callback * Add ProductImageDialog widget and update ProductScreen to use it
1 parent 39e734f commit 81ca47e

File tree

6 files changed

+96
-99
lines changed

6 files changed

+96
-99
lines changed

example/lib/src/common/router/router_state_mixin.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import 'package:example/src/common/router/home_guard.dart';
44
import 'package:example/src/common/router/routes.dart';
55
import 'package:example/src/common/router/shop_guard.dart';
66
import 'package:example/src/feature/shop/data/shop_tabs_cache_service.dart';
7-
import 'package:flutter/widgets.dart' show State, StatefulWidget, ValueNotifier;
7+
import 'package:flutter/widgets.dart'
8+
show DefaultTransitionDelegate, State, StatefulWidget, ValueNotifier;
89
import 'package:octopus/octopus.dart';
910

1011
mixin RouterStateMixin<T extends StatefulWidget> on State<T> {
@@ -30,6 +31,7 @@ mixin RouterStateMixin<T extends StatefulWidget> on State<T> {
3031
router = Octopus(
3132
routes: Routes.values,
3233
defaultRoute: Routes.home,
34+
transitionDelegate: const DefaultTransitionDelegate<void>(),
3335
guards: <IOctopusGuard>[
3436
// Check authentication.
3537
AuthenticationGuard(

example/lib/src/common/router/routes.dart

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:example/src/feature/shop/widget/catalog_screen.dart';
1111
import 'package:example/src/feature/shop/widget/category_screen.dart';
1212
import 'package:example/src/feature/shop/widget/checkout_screen.dart';
1313
import 'package:example/src/feature/shop/widget/favorites_screen.dart';
14+
import 'package:example/src/feature/shop/widget/product_image_dialog.dart';
1415
import 'package:example/src/feature/shop/widget/product_screen.dart';
1516
import 'package:example/src/feature/shop/widget/shop_screen.dart';
1617
import 'package:flutter/material.dart';
@@ -24,6 +25,7 @@ enum Routes with OctopusRoute {
2425
catalog('catalog', title: 'Catalog'),
2526
category('category', title: 'Category'),
2627
product('product', title: 'Product'),
28+
productImageDialog('product-img-dialog', title: 'Product\'s Image'),
2729
basket('basket', title: 'Basket'),
2830
checkout('checkout', title: 'Checkout'),
2931
favorites('favorites', title: 'Favorites'),
@@ -51,6 +53,10 @@ enum Routes with OctopusRoute {
5153
Routes.catalog => const CatalogScreen(),
5254
Routes.category => CategoryScreen(id: node.arguments['id']),
5355
Routes.product => ProductScreen(id: node.arguments['id']),
56+
Routes.productImageDialog => ProductImageDialog(
57+
id: node.arguments['id'],
58+
idx: node.arguments['idx'],
59+
),
5460
Routes.basket => const BasketScreen(),
5561
Routes.checkout => const CheckoutScreen(),
5662
Routes.favorites => const FavoritesScreen(),
@@ -68,29 +74,4 @@ enum Routes with OctopusRoute {
6874
? CustomUserPage()
6975
: super.pageBuilder(context, node);
7076
*/
71-
72-
/// Pushes the [route] to the catalog tab.
73-
/// [id] is the product or category id for the [route].
74-
static void pushToCatalog(BuildContext context, Routes route, String id) =>
75-
context.octopus.setState((state) {
76-
final node = state.find((n) => n.name == 'catalog-tab');
77-
if (node == null) {
78-
return state
79-
..removeByName(Routes.shop.name)
80-
..add(Routes.shop.node(
81-
children: <OctopusNode>[
82-
OctopusNode.mutable(
83-
'catalog-tab',
84-
children: <OctopusNode>[
85-
Routes.catalog.node(),
86-
route.node(arguments: {'id': id}),
87-
],
88-
),
89-
],
90-
))
91-
..arguments['shop'] = 'catalog';
92-
}
93-
node.children.add(route.node(arguments: {'id': id}));
94-
return state..arguments['shop'] = 'catalog';
95-
});
9677
}

example/lib/src/feature/shop/widget/catalog_screen.dart

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -185,17 +185,33 @@ class _CatalogTile extends StatelessWidget {
185185
textAlign: TextAlign.center,
186186
style: Theme.of(context).textTheme.bodySmall,
187187
),
188-
onTap: () => Routes.pushToCatalog(
189-
context,
190-
Routes.category,
191-
category.id,
192-
),
193-
/* onTap: () => context.octopus.setState(
194-
(state) => state
195-
..add(Routes.category.node(
196-
arguments: <String, String>{'id': category.id},
197-
)),
198-
), */
188+
onTap: () => context.octopus.setState((state) {
189+
// It's a bit overcomplicated just for the sake of example.
190+
// You can just use:
191+
// ```dart
192+
// state
193+
// ..findByName('catalog-tab')?.add(Routes.category.node(arguments: {'id': category.id}))
194+
// ..arguments['shop'] = 'catalog';
195+
// ```
196+
197+
// Find or add the `shop` node
198+
// Find or add `catalog-tab` node inside it
199+
// Check if the `catalog` node is already added
200+
// Add the `catalog` node
201+
state
202+
.putIfAbsent(
203+
Routes.shop.name,
204+
Routes.shop.node,
205+
)
206+
.putIfAbsent(
207+
'catalog-tab',
208+
() => OctopusNode.mutable('catalog-tab'),
209+
)
210+
..putIfAbsent(Routes.catalog.name, Routes.catalog.node)
211+
..add(Routes.category.node(arguments: {'id': category.id}));
212+
// Switch to the `catalog` tab
213+
return state..arguments['shop'] = 'catalog';
214+
}),
199215
);
200216
}
201217

example/lib/src/feature/shop/widget/product_image_screen.dart renamed to example/lib/src/feature/shop/widget/product_image_dialog.dart

Lines changed: 51 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
import 'dart:math' as math;
2+
3+
import 'package:example/src/common/constant/config.dart';
14
import 'package:example/src/common/widget/not_found_screen.dart';
2-
import 'package:example/src/feature/shop/model/product.dart';
35
import 'package:example/src/feature/shop/widget/shop_scope.dart';
6+
import 'package:flutter/foundation.dart';
47
import 'package:flutter/material.dart';
58
import 'package:flutter/services.dart';
69
import 'package:photo_view/photo_view.dart';
710

8-
/// {@template photo_image_screen}
9-
/// ProductImageViewScreen widget
11+
/// {@template photo_image_dialog}
12+
/// ProductImageDialog widget
1013
/// {@endtemplate}
11-
class ProductImageScreen extends StatelessWidget {
12-
/// {@macro photo_image_screen}
13-
const ProductImageScreen._({
14+
class ProductImageDialog extends StatelessWidget {
15+
/// {@macro photo_image_dialog}
16+
const ProductImageDialog({
1417
required this.id,
1518
required this.idx,
1619
super.key, // ignore: unused_element
@@ -22,30 +25,6 @@ class ProductImageScreen extends StatelessWidget {
2225
/// Image index in product images
2326
final Object? idx;
2427

25-
/// Show anonymous route screen
26-
static Future<void> show(
27-
BuildContext context, {
28-
required ProductID id,
29-
required int index,
30-
}) {
31-
final navigator = Navigator.of(context, rootNavigator: true);
32-
final route = PageRouteBuilder<void>(
33-
pageBuilder: (context, _, __) => BackButtonListener(
34-
onBackButtonPressed: navigator.maybePop,
35-
child: ProductImageScreen._(id: id, idx: index),
36-
),
37-
transitionsBuilder: (context, animation, secondayAnimation, child) =>
38-
ScaleTransition(
39-
scale: Tween<double>(begin: 1.25, end: 1).animate(animation),
40-
child: FadeTransition(
41-
opacity: animation.drive(CurveTween(curve: Curves.easeIn)),
42-
child: child,
43-
),
44-
),
45-
);
46-
return navigator.push<void>(route);
47-
}
48-
4928
@override
5029
Widget build(BuildContext context) {
5130
const notFoundScreen = NotFoundScreen(
@@ -66,37 +45,53 @@ class ProductImageScreen extends StatelessWidget {
6645
};
6746
if (index == null) return notFoundScreen;
6847
if (index < 0 || index >= product.images.length) return notFoundScreen;
69-
final image = product.images[index];
48+
final image = (!kIsWeb || Config.environment.isDevelopment
49+
? AssetImage(product.images[index])
50+
: NetworkImage('/${product.images[index]}')) as ImageProvider<Object>;
7051

71-
return Scaffold(
72-
resizeToAvoidBottomInset: false,
73-
body: SizedBox.expand(
74-
child: Stack(
75-
children: <Widget>[
76-
Positioned.fill(
77-
child: PhotoView.customChild(
78-
basePosition: Alignment.center,
79-
initialScale: PhotoViewComputedScale.contained,
80-
minScale: PhotoViewComputedScale.contained,
81-
maxScale: 3.0,
82-
enableRotation: false,
83-
backgroundDecoration:
84-
const BoxDecoration(color: Colors.transparent),
85-
child: SafeArea(
86-
child: Center(
87-
child: Hero(
88-
tag: 'product-${product.id}-image-$index',
89-
child: Image.asset(
90-
image,
91-
fit: BoxFit.fitHeight,
52+
return Dialog(
53+
shape: const RoundedRectangleBorder(
54+
borderRadius: BorderRadius.all(Radius.circular(24)),
55+
),
56+
child: ClipRRect(
57+
borderRadius: const BorderRadius.all(Radius.circular(24)),
58+
child: SizedBox.square(
59+
dimension: math.min(MediaQuery.sizeOf(context).shortestSide, 400),
60+
child: Stack(
61+
children: <Widget>[
62+
Positioned.fill(
63+
child: PhotoView.customChild(
64+
basePosition: Alignment.center,
65+
initialScale: PhotoViewComputedScale.contained,
66+
minScale: PhotoViewComputedScale.contained,
67+
maxScale: 3.0,
68+
enableRotation: false,
69+
backgroundDecoration:
70+
const BoxDecoration(color: Colors.transparent),
71+
child: SafeArea(
72+
child: Center(
73+
child: Material(
74+
color: Colors.transparent,
75+
child: Hero(
76+
tag: 'product-${product.id}-image-$index',
77+
child: Material(
78+
color: Colors.transparent,
79+
child: Image(
80+
image: image,
81+
width: 400,
82+
height: 400,
83+
fit: BoxFit.fitHeight,
84+
),
85+
),
86+
),
9287
),
9388
),
9489
),
9590
),
9691
),
97-
),
98-
if (Navigator.canPop(context)) const _ProductImageBackButton(),
99-
],
92+
if (Navigator.canPop(context)) const _ProductImageBackButton(),
93+
],
94+
),
10095
),
10196
),
10297
);
@@ -131,7 +126,7 @@ class _ProductImageBackButton extends StatelessWidget {
131126
},
132127
customBorder: const CircleBorder(),
133128
child: Icon(
134-
Icons.fullscreen_exit,
129+
Icons.close,
135130
size: _isLarge ? 48 : 32,
136131
color: Theme.of(context).colorScheme.onSecondary,
137132
),

example/lib/src/feature/shop/widget/product_screen.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:math' as math;
33

44
import 'package:collection/collection.dart';
55
import 'package:example/src/common/constant/config.dart';
6+
import 'package:example/src/common/router/routes.dart';
67
import 'package:example/src/common/util/color_util.dart';
78
import 'package:example/src/common/widget/common_actions.dart';
89
import 'package:example/src/common/widget/form_placeholder.dart';
@@ -12,13 +13,13 @@ import 'package:example/src/common/widget/scaffold_padding.dart';
1213
import 'package:example/src/feature/shop/model/product.dart';
1314
import 'package:example/src/feature/shop/widget/catalog_breadcrumbs.dart';
1415
import 'package:example/src/feature/shop/widget/favorite_button.dart';
15-
import 'package:example/src/feature/shop/widget/product_image_screen.dart';
1616
import 'package:example/src/feature/shop/widget/shop_back_button.dart';
1717
import 'package:example/src/feature/shop/widget/shop_scope.dart';
1818
import 'package:flutter/foundation.dart';
1919
import 'package:flutter/material.dart';
2020
import 'package:flutter/services.dart';
2121
import 'package:google_fonts/google_fonts.dart';
22+
import 'package:octopus/octopus.dart';
2223

2324
/// {@template product_screen}
2425
/// ProductScreen widget.
@@ -803,10 +804,12 @@ class _ProductPhotosListViewState extends State<_ProductPhotosListView> {
803804
color: Colors.transparent,
804805
child: InkWell(
805806
onTap: () {
806-
ProductImageScreen.show(
807-
context,
808-
id: widget.product.id,
809-
index: idx,
807+
context.octopus.push(
808+
Routes.productImageDialog,
809+
arguments: <String, String>{
810+
'id': widget.product.id.toString(),
811+
'idx': idx.toString(),
812+
},
810813
);
811814
HapticFeedback.mediumImpact().ignore();
812815
},

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ packages:
498498
path: ".."
499499
relative: true
500500
source: path
501-
version: "0.0.1-pre.2"
501+
version: "0.0.2"
502502
package_config:
503503
dependency: transitive
504504
description:

0 commit comments

Comments
 (0)