-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Feat cover image reposition(#2462) #8102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 11 commits
8199139
bed09e0
cda7c13
6e1f9b6
a445eb1
4bd8638
15b4089
e72f53b
3094dee
23c0b15
b6625b8
6555c8d
20586e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,227 @@ | ||||||||||||||||||||||||||||||||||||||
| import 'package:flutter/material.dart'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| class DesktopCoverAlignController extends ChangeNotifier { | ||||||||||||||||||||||||||||||||||||||
| DesktopCoverAlignController(String? offset) { | ||||||||||||||||||||||||||||||||||||||
| double x = 0; | ||||||||||||||||||||||||||||||||||||||
| double y = 0; | ||||||||||||||||||||||||||||||||||||||
| if (offset != null) { | ||||||||||||||||||||||||||||||||||||||
| final splits = offset.split(','); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| x = double.parse(splits.first); | ||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||
| x = 0; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| y = double.parse(splits.last); | ||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||
| y = 0; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| _initialAlignment = Alignment(x, y); | ||||||||||||||||||||||||||||||||||||||
| _adjustedAlign = _initialAlignment; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| late final Alignment _initialAlignment; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| late Alignment _adjustedAlign; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Alignment get alignment => _adjustedAlign; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void reset() { | ||||||||||||||||||||||||||||||||||||||
| _adjustedAlign = Alignment.center; | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+32
to
+33
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): reset() always sets alignment to center, not to initial value. reset() should set _adjustedAlign to _initialAlignment to properly restore the original alignment. |
||||||||||||||||||||||||||||||||||||||
| notifyListeners(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void cancel() { | ||||||||||||||||||||||||||||||||||||||
| _adjustedAlign = _initialAlignment; | ||||||||||||||||||||||||||||||||||||||
| notifyListeners(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void changeAlign(double x, double y) { | ||||||||||||||||||||||||||||||||||||||
| _adjustedAlign = Alignment(x, y); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+42
to
+43
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): changeAlign does not notify listeners after updating alignment. Please add notifyListeners() at the end of changeAlign to ensure UI updates when alignment changes. |
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| bool get isModified => _adjustedAlign != _initialAlignment; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| String getAlignAttribute() { | ||||||||||||||||||||||||||||||||||||||
| return "${_adjustedAlign.x.toStringAsFixed(1)},${_adjustedAlign.y.toStringAsFixed(1)}"; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| class DesktopCoverAlign extends StatefulWidget { | ||||||||||||||||||||||||||||||||||||||
| const DesktopCoverAlign({ | ||||||||||||||||||||||||||||||||||||||
| super.key, | ||||||||||||||||||||||||||||||||||||||
| required this.controller, | ||||||||||||||||||||||||||||||||||||||
| required this.imageProvider, | ||||||||||||||||||||||||||||||||||||||
| this.fit = BoxFit.cover, | ||||||||||||||||||||||||||||||||||||||
| this.alignEnable = false, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| final DesktopCoverAlignController controller; | ||||||||||||||||||||||||||||||||||||||
| final ImageProvider imageProvider; | ||||||||||||||||||||||||||||||||||||||
| final BoxFit fit; | ||||||||||||||||||||||||||||||||||||||
| final bool alignEnable; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| State<DesktopCoverAlign> createState() => _DesktopCoverAlignState(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| class _DesktopCoverAlignState extends State<DesktopCoverAlign> { | ||||||||||||||||||||||||||||||||||||||
| ImageStreamListener? _imageStreamListener; | ||||||||||||||||||||||||||||||||||||||
| ImageStream? _imageStream; | ||||||||||||||||||||||||||||||||||||||
| Size? _imageSize; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Size? _frameSize; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| double x = 0; | ||||||||||||||||||||||||||||||||||||||
| double y = 0; | ||||||||||||||||||||||||||||||||||||||
| late final DesktopCoverAlignController controller; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| void initState() { | ||||||||||||||||||||||||||||||||||||||
| super.initState(); | ||||||||||||||||||||||||||||||||||||||
| controller = widget.controller; | ||||||||||||||||||||||||||||||||||||||
| final alignment = controller.alignment; | ||||||||||||||||||||||||||||||||||||||
| x = alignment.x; | ||||||||||||||||||||||||||||||||||||||
| y = alignment.y; | ||||||||||||||||||||||||||||||||||||||
| controller.addListener(updateAlign); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| void dispose() { | ||||||||||||||||||||||||||||||||||||||
| controller.removeListener(updateAlign); | ||||||||||||||||||||||||||||||||||||||
| super.dispose(); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| _stopImageStream(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| void didChangeDependencies() { | ||||||||||||||||||||||||||||||||||||||
| _resolveImage(); | ||||||||||||||||||||||||||||||||||||||
| super.didChangeDependencies(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| void didUpdateWidget(DesktopCoverAlign oldWidget) { | ||||||||||||||||||||||||||||||||||||||
| if (widget.imageProvider != oldWidget.imageProvider) { | ||||||||||||||||||||||||||||||||||||||
| controller.reset(); | ||||||||||||||||||||||||||||||||||||||
| _resolveImage(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| super.didUpdateWidget(oldWidget); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void updateAlign() { | ||||||||||||||||||||||||||||||||||||||
| setState(() { | ||||||||||||||||||||||||||||||||||||||
| x = controller.alignment.x; | ||||||||||||||||||||||||||||||||||||||
| y = controller.alignment.y; | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void _resolveImage() { | ||||||||||||||||||||||||||||||||||||||
| final ImageStream newStream = widget.imageProvider.resolve( | ||||||||||||||||||||||||||||||||||||||
| const ImageConfiguration(), | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| _updateSourceStream(newStream); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ImageStreamListener _getOrCreateListener() { | ||||||||||||||||||||||||||||||||||||||
| void handleImageFrame(ImageInfo info, bool synchronousCall) { | ||||||||||||||||||||||||||||||||||||||
| void setupCB() { | ||||||||||||||||||||||||||||||||||||||
| _imageSize = Size( | ||||||||||||||||||||||||||||||||||||||
| info.image.width.toDouble(), | ||||||||||||||||||||||||||||||||||||||
| info.image.height.toDouble(), | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| synchronousCall ? setupCB() : setState(setupCB); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| _imageStreamListener = ImageStreamListener( | ||||||||||||||||||||||||||||||||||||||
| handleImageFrame, | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return _imageStreamListener!; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void _updateSourceStream(ImageStream newStream) { | ||||||||||||||||||||||||||||||||||||||
| if (_imageStream?.key == newStream.key) { | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (_imageStreamListener != null) { | ||||||||||||||||||||||||||||||||||||||
| _imageStream?.removeListener(_imageStreamListener!); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| _imageStream = newStream; | ||||||||||||||||||||||||||||||||||||||
| _imageStream!.addListener(_getOrCreateListener()); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void _stopImageStream() { | ||||||||||||||||||||||||||||||||||||||
| if (_imageStreamListener != null) { | ||||||||||||||||||||||||||||||||||||||
| _imageStream?.removeListener(_imageStreamListener!); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void _changeAlignOffset(Offset offset) { | ||||||||||||||||||||||||||||||||||||||
| setState(() { | ||||||||||||||||||||||||||||||||||||||
| if (_imageSize == null || _frameSize == null) return; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| final imageRatio = _imageSize!.aspectRatio; | ||||||||||||||||||||||||||||||||||||||
| final frameRatio = _frameSize!.aspectRatio; | ||||||||||||||||||||||||||||||||||||||
| final isVertical = imageRatio < frameRatio; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| final imageFrameHeight = | ||||||||||||||||||||||||||||||||||||||
| _frameSize!.width / _imageSize!.width * _imageSize!.height; | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+172
to
+173
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): Potential division by zero in imageFrameHeight and imageFrameWidth calculations. Add checks to ensure _imageSize!.width and _imageSize!.height are not zero before performing these calculations. |
||||||||||||||||||||||||||||||||||||||
| final imageFrameWidth = | ||||||||||||||||||||||||||||||||||||||
| _frameSize!.height / _imageSize!.height * _imageSize!.width; | ||||||||||||||||||||||||||||||||||||||
| final exceedWidth = imageFrameWidth - _frameSize!.width; | ||||||||||||||||||||||||||||||||||||||
| final exceedHeight = imageFrameHeight - _frameSize!.height; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (isVertical) { | ||||||||||||||||||||||||||||||||||||||
| final targetY = y + offset.dy / exceedHeight * 2; | ||||||||||||||||||||||||||||||||||||||
| if (targetY >= -1 && targetY <= 1) { | ||||||||||||||||||||||||||||||||||||||
| y = targetY; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| final targetX = x + offset.dx / exceedWidth * 2; | ||||||||||||||||||||||||||||||||||||||
| if (targetX >= -1 && targetX <= 1) { | ||||||||||||||||||||||||||||||||||||||
| x = targetX; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+179
to
+189
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (bug_risk): No clamping for x/y values outside [-1, 1] range. Clamping targetX and targetY to [-1, 1] would ensure the UI remains responsive even with large or fast drags.
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| widget.controller.changeAlign(x, y); | ||||||||||||||||||||||||||||||||||||||
| setState(() {}); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| Widget build(BuildContext context) { | ||||||||||||||||||||||||||||||||||||||
| return LayoutBuilder( | ||||||||||||||||||||||||||||||||||||||
| builder: (context, constraints) { | ||||||||||||||||||||||||||||||||||||||
| _frameSize = | ||||||||||||||||||||||||||||||||||||||
| Size(constraints.biggest.width, constraints.biggest.height); | ||||||||||||||||||||||||||||||||||||||
| _imageSize ??= _frameSize; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Widget child = Image( | ||||||||||||||||||||||||||||||||||||||
| image: widget.imageProvider, | ||||||||||||||||||||||||||||||||||||||
| width: _frameSize!.width, | ||||||||||||||||||||||||||||||||||||||
| height: _frameSize!.height, | ||||||||||||||||||||||||||||||||||||||
| fit: widget.fit, | ||||||||||||||||||||||||||||||||||||||
| alignment: Alignment(-x, -y), | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| if (widget.alignEnable && _imageSize != null) { | ||||||||||||||||||||||||||||||||||||||
| child = GestureDetector( | ||||||||||||||||||||||||||||||||||||||
| onHorizontalDragUpdate: (details) { | ||||||||||||||||||||||||||||||||||||||
| final delta = details.delta; | ||||||||||||||||||||||||||||||||||||||
| _changeAlignOffset(delta); | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| onVerticalDragUpdate: (details) { | ||||||||||||||||||||||||||||||||||||||
| final delta = details.delta; | ||||||||||||||||||||||||||||||||||||||
| _changeAlignOffset(delta); | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| child: child, | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| return child; | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): reset() on coverAlignController may not restore previous alignment.
Currently, reset() always sets alignment to center, which may not match the new coverDetails. Please update the controller to initialize alignment based on the new coverDetails.