Compare commits

...

2 Commits

View File

@ -1,4 +1,3 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -22,19 +21,10 @@ class CropBackgroundPage extends StatefulWidget {
CropBackgroundPageState createState() => CropBackgroundPageState();
}
// TODO(PapaTutuWawa): Replace the custom code with InteractiveViewer, once
// https://github.com/flutter/flutter/issues/107855 gets fixed.
class CropBackgroundPageState extends State<CropBackgroundPage> {
CropBackgroundPageState() : _x = 0, _y = 0, _track = false, super();
double _x = 0;
double _y = 0;
bool _track = false;
double _scale = -1;
double _scaleExtra = 0;
double? _scaleNOld;
double _scaleNNew = 1;
CropBackgroundPageState() : super();
double? _scalingFactorCached;
TransformationController? _controller;
double _scalingFactor(BuildContext context, CropBackgroundState state) {
if (_scalingFactorCached != null) return _scalingFactorCached!;
@ -61,35 +51,36 @@ class CropBackgroundPageState extends State<CropBackgroundPage> {
);
}
Widget image;
if (state.blurEnabled) {
return Positioned(
top: _y,
left: _x,
child: ImageFiltered(
imageFilter: ImageFilter.blur(
sigmaX: 10,
sigmaY: 10,
),
child: Image.memory(
state.image!,
width: state.imageWidth * _scale * _scaleExtra,
height: state.imageHeight * _scale * _scaleExtra,
fit: BoxFit.contain,
),
image = ImageFiltered(
imageFilter: ImageFilter.blur(
sigmaX: 10,
sigmaY: 10,
),
);
} else {
return Positioned(
top: _y,
left: _x,
child: Image.memory(
state.image!,
width: state.imageWidth * (_scale + _scaleExtra),
height: state.imageHeight * (_scale + _scaleExtra),
fit: BoxFit.contain,
),
);
} else {
image = Image.memory(
state.image!,
fit: BoxFit.contain,
);
}
final q = _scalingFactor(context, state);
_controller ??= TransformationController(Matrix4.identity()..scale(q, q, 1));
return InteractiveViewer(
constrained: false,
maxScale: 4,
minScale: 1,
panEnabled: !state.isWorking,
scaleEnabled: !state.isWorking,
transformationController: _controller,
child: image,
);
}
Widget _buildLoadingSpinner(CropBackgroundState state) {
@ -113,13 +104,8 @@ class CropBackgroundPageState extends State<CropBackgroundPage> {
@override
Widget build(BuildContext context) {
final query = MediaQuery.of(context);
return BlocBuilder<CropBackgroundBloc, CropBackgroundState>(
builder: (BuildContext context, CropBackgroundState state) {
if (_scale == -1 && state.imageWidth != 0 && state.imageHeight != 0) {
_scale = _scalingFactor(context, state);
}
return WillPopScope(
onWillPop: () async {
if (state.isWorking) return false;
@ -128,129 +114,94 @@ class CropBackgroundPageState extends State<CropBackgroundPage> {
return true;
},
child: SafeArea(
child: GestureDetector(
onScaleStart: (_) => _track = true,
onScaleEnd: (_) {
_track = false;
_scale += _scaleExtra;
_scaleExtra = 0;
_scaleNOld = null;
},
onScaleUpdate: (event) {
if (!_track) return;
setState(() {
_x = min(
max(
_x + event.focalPointDelta.dx,
query.size.width - state.imageWidth * _scale,
child: Stack(
children: [
// ignore: prefer_if_elements_to_conditional_expressions
state.imageHeight != 0 && state.imageWidth != 0 ?
_buildImage(context, state) :
const SizedBox(),
Positioned(
top: 8,
left: 8,
child: Material(
color: const Color.fromRGBO(0, 0, 0, 0),
child: CancelButton(
onPressed: () {
context.read<CropBackgroundBloc>().add(CropBackgroundResetEvent());
context.read<NavigationBloc>().add(PoppedRouteEvent());
},
),
),
),
Positioned(
top: 8,
right: 8,
child: Material(
color: const Color.fromRGBO(0, 0, 0, 0),
child: DecoratedBox(
decoration: BoxDecoration(
color: const Color.fromRGBO(0, 0, 0, 0.6),
borderRadius: BorderRadius.circular(24),
),
0,
);
_y = min(
max(
_y + event.focalPointDelta.dy,
query.size.height - state.imageHeight * _scale,
child: IntrinsicWidth(
child: Row(
children: [
const Padding(
padding: EdgeInsets.only(left: 8),
child: Text('Blur background'),
),
Switch(
value: state.blurEnabled,
onChanged: (_) {
if (state.isWorking) return;
context.read<CropBackgroundBloc>()
.add(BlurToggledEvent());
},
),
],
),
),
0,
);
),
),
),
Positioned(
left: 0,
right: 0,
bottom: 8,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RoundedButton(
cornerRadius: 100,
onTap: () {
final q = _scalingFactor(context, state);
final value = _controller == null ?
(Matrix4.identity()..scale(q, q, 1)) :
_controller!.value;
final translation = value.getTranslation();
final scale = _controller == null ?
1.0 :
value.entry(0, 0);
if (event.pointerCount == 2) {
if (_scaleNOld == null) {
_scaleNOld = event.scale;
_scaleNNew = event.scale;
_scaleExtra = 0;
} else {
_scaleNOld = _scaleNNew;
_scaleNNew = event.scale;
}
if (_scaleExtra + _scaleNNew - _scaleNOld! + _scale >= _scalingFactor(context, state)) {
_scaleExtra += _scaleNNew - _scaleNOld!;
}
}
});
},
child: Stack(
children: [
// ignore: prefer_if_elements_to_conditional_expressions
state.imageHeight != 0 && state.imageWidth != 0 ?
_buildImage(context, state) :
const SizedBox(),
Positioned(
top: 8,
left: 8,
child: Material(
color: const Color.fromRGBO(0, 0, 0, 0),
child: CancelButton(
onPressed: () {
context.read<CropBackgroundBloc>().add(CropBackgroundResetEvent());
context.read<NavigationBloc>().add(PoppedRouteEvent());
context.read<CropBackgroundBloc>().add(
BackgroundSetEvent(
translation.x,
translation.y,
scale,
MediaQuery.of(context).size.height,
MediaQuery.of(context).size.width,
),
);
},
enabled: !state.isWorking,
child: const Text('Set as background image'),
),
),
],
),
Positioned(
top: 8,
right: 8,
child: Material(
color: const Color.fromRGBO(0, 0, 0, 0),
child: DecoratedBox(
decoration: BoxDecoration(
color: const Color.fromRGBO(0, 0, 0, 0.6),
borderRadius: BorderRadius.circular(24),
),
child: IntrinsicWidth(
child: Row(
children: [
const Padding(
padding: EdgeInsets.only(left: 8),
child: Text('Blur background'),
),
Switch(
value: state.blurEnabled,
onChanged: (_) {
if (state.isWorking) return;
context.read<CropBackgroundBloc>()
.add(BlurToggledEvent());
},
),
],
),
),
),
),
),
Positioned(
left: 0,
right: 0,
bottom: 8,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RoundedButton(
cornerRadius: 100,
onTap: () {
context.read<CropBackgroundBloc>().add(
BackgroundSetEvent(
_x,
_y,
_scale,
MediaQuery.of(context).size.height,
MediaQuery.of(context).size.width,
),
);
},
enabled: !state.isWorking,
child: const Text('Set as background image'),
),
],
),
),
_buildLoadingSpinner(state),
],
),
),
_buildLoadingSpinner(state),
],
),
),
);