[mobile] Fix Image editor (#6819)

## Description
- Fix bottom navigation bar color in light theme
- Fix initial color in paint editor
- Tap to reset tune adjustment value (brightness, exposure, etc..) and
add haptics when crossing zero
This commit is contained in:
Neeraj
2025-08-12 08:55:22 +05:30
committed by GitHub
11 changed files with 81 additions and 44 deletions

View File

@@ -224,17 +224,17 @@ extension CustomColorScheme on ColorScheme {
Color get videoPlayerPrimaryColor => brightness == Brightness.light
? const Color.fromRGBO(0, 179, 60, 1)
: const Color.fromRGBO(1, 222, 77, 1);
Color get videoPlayerBackgroundColor => brightness == Brightness.light
? const Color(0xFFF5F5F5)
: const Color(0xFF252525);
Color get videoPlayerBorderColor => brightness == Brightness.light
? const Color(0xFF424242)
: const Color(0xFFFFFFFF);
Color get imageEditorPrimaryColor => const Color.fromRGBO(8, 194, 37, 1);
Color get editorBackgroundColor => brightness == Brightness.light
? const Color(0xFFF5F5F5)
: const Color(0xFF252525);
Color get defaultBackgroundColor =>
brightness == Brightness.light ? backgroundBaseLight : backgroundBaseDark;

View File

@@ -3,7 +3,7 @@ import "package:flutter_svg/svg.dart";
import "package:photos/ente_theme_data.dart";
import "package:photos/theme/ente_theme.dart";
class CircularIconButton extends StatelessWidget {
class CircularIconButton extends StatelessWidget {
final String label;
final VoidCallback onTap;
final String? svgPath;
@@ -11,7 +11,7 @@ class CircularIconButton extends StatelessWidget {
final bool isSelected;
const CircularIconButton({
super.key,
super.key,
required this.label,
required this.onTap,
this.svgPath,
@@ -40,12 +40,12 @@ class CircularIconButton extends StatelessWidget {
.colorScheme
.imageEditorPrimaryColor
.withOpacity(0.24)
: colorScheme.backgroundElevated2,
: Theme.of(context).colorScheme.editorBackgroundColor,
shape: BoxShape.circle,
border: Border.all(
color: isSelected
? Theme.of(context).colorScheme.imageEditorPrimaryColor
: colorScheme.backgroundElevated2,
: Theme.of(context).colorScheme.editorBackgroundColor,
width: 2,
),
),

View File

@@ -1,5 +1,5 @@
import "package:flutter/material.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ente_theme_data.dart";
class ImageEditorColorPicker extends StatefulWidget {
final double value;
@@ -23,7 +23,6 @@ class ColorSliderState extends State<ImageEditorColorPicker> {
@override
Widget build(BuildContext context) {
final colorScheme = getEnteColorScheme(context);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: SizedBox(
@@ -55,7 +54,7 @@ class ColorSliderState extends State<ImageEditorColorPicker> {
end: Alignment.centerRight,
),
border: Border.all(
color: colorScheme.backgroundElevated2,
color: Theme.of(context).colorScheme.editorBackgroundColor,
width: 6,
),
),

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import "package:flutter_svg/svg.dart";
import "package:photos/ente_theme_data.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/tools/editor/image_editor/circular_icon_button.dart";
@@ -191,7 +192,7 @@ class CropAspectChip extends StatelessWidget {
decoration: BoxDecoration(
color: isSelected
? colorScheme.fillBasePressed
: colorScheme.backgroundElevated2,
: Theme.of(context).colorScheme.editorBackgroundColor,
borderRadius: BorderRadius.circular(25),
),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),

View File

@@ -1,10 +1,10 @@
import "dart:async";
import "dart:io";
import "dart:math";
import "dart:typed_data";
import 'dart:ui' as ui show Image;
import 'package:flutter/material.dart';
import "package:flutter/services.dart";
import "package:flutter_image_compress/flutter_image_compress.dart";
import "package:flutter_svg/svg.dart";
import "package:logging/logging.dart";
@@ -176,17 +176,18 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
final isLightMode = Theme.of(context).brightness == Brightness.light;
final colorScheme = getEnteColorScheme(context);
final textTheme = getEnteTextTheme(context);
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: colorScheme.backgroundBase,
body: PopScope(
canPop: false,
onPopInvoked: (didPop) {
if (didPop) return;
editorKey.currentState?.disablePopScope = true;
_showExitConfirmationDialog(context);
},
child: ProImageEditor.file(
return PopScope(
canPop: false,
onPopInvoked: (didPop) {
if (didPop) return;
editorKey.currentState?.disablePopScope = true;
_showExitConfirmationDialog(context);
},
child: Scaffold(
extendBodyBehindAppBar: true,
resizeToAvoidBottomInset: false,
backgroundColor: colorScheme.backgroundBase,
body: ProImageEditor.file(
key: editorKey,
widget.file,
callbacks: ProImageEditorCallbacks(
@@ -205,6 +206,14 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
),
configs: ProImageEditorConfigs(
imageEditorTheme: ImageEditorTheme(
uiOverlayStyle: SystemUiOverlayStyle(
systemNavigationBarContrastEnforced: true,
systemNavigationBarColor: Colors.transparent,
statusBarBrightness:
isLightMode ? Brightness.dark : Brightness.light,
statusBarIconBrightness:
isLightMode ? Brightness.dark : Brightness.light,
),
appBarBackgroundColor: colorScheme.backgroundBase,
background: colorScheme.backgroundBase,
bottomBarBackgroundColor: colorScheme.backgroundBase,
@@ -212,6 +221,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
background: colorScheme.backgroundBase,
),
paintingEditor: PaintingEditorTheme(
initialColor: const Color(0xFF00FFFF),
background: colorScheme.backgroundBase,
),
textEditor: const TextEditorTheme(
@@ -227,6 +237,12 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
background: colorScheme.backgroundBase,
),
emojiEditor: EmojiEditorTheme(
bottomActionBarConfig: BottomActionBarConfig(
showSearchViewButton: true,
buttonColor: colorScheme.backgroundBase,
buttonIconColor: colorScheme.tabIcon,
backgroundColor: colorScheme.backgroundBase,
),
backgroundColor: colorScheme.backgroundBase,
),
),

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import "package:flutter_svg/svg.dart";
import "package:photos/ente_theme_data.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/tools/editor/image_editor/circular_icon_button.dart";
@@ -174,7 +175,7 @@ class _FontPickerWidget extends StatelessWidget {
decoration: BoxDecoration(
color: isSelected
? colorScheme.fillBasePressed
: colorScheme.backgroundElevated2,
: Theme.of(context).colorScheme.editorBackgroundColor,
borderRadius: BorderRadius.circular(25),
),
child: Center(
@@ -209,7 +210,7 @@ class _BackgroundPickerWidget extends StatelessWidget {
'text': 'Aa',
'selectedBackgroundColor':
isLightMode ? colorScheme.fillFaint : Colors.white,
'backgroundColor': colorScheme.backgroundElevated2,
'backgroundColor': Theme.of(context).colorScheme.editorBackgroundColor,
'border': null,
'textColor': Colors.white,
'selectedInnerBackgroundColor': Colors.black,
@@ -219,7 +220,7 @@ class _BackgroundPickerWidget extends StatelessWidget {
'text': 'Aa',
'selectedBackgroundColor':
isLightMode ? colorScheme.fillFaint : Colors.white,
'backgroundColor': colorScheme.backgroundElevated2,
'backgroundColor': Theme.of(context).colorScheme.editorBackgroundColor,
'border': null,
'textColor': Colors.black,
'selectedInnerBackgroundColor': Colors.transparent,
@@ -229,7 +230,7 @@ class _BackgroundPickerWidget extends StatelessWidget {
'text': 'Aa',
'selectedBackgroundColor':
isLightMode ? colorScheme.fillFaint : Colors.white,
'backgroundColor': colorScheme.backgroundElevated2,
'backgroundColor': Theme.of(context).colorScheme.editorBackgroundColor,
'border': null,
'textColor': Colors.black,
'selectedInnerBackgroundColor': Colors.black.withOpacity(0.11),
@@ -241,7 +242,7 @@ class _BackgroundPickerWidget extends StatelessWidget {
'text': 'Aa',
'selectedBackgroundColor':
isLightMode ? colorScheme.fillFaint : Colors.black,
'backgroundColor': colorScheme.backgroundElevated2,
'backgroundColor': Theme.of(context).colorScheme.editorBackgroundColor,
'border':
isLightMode ? null : Border.all(color: Colors.white, width: 2),
'textColor': Colors.black,
@@ -354,7 +355,7 @@ class _AlignPickerWidget extends StatelessWidget {
decoration: BoxDecoration(
color: isSelected
? colorScheme.fillBasePressed
: colorScheme.backgroundElevated2,
: Theme.of(context).colorScheme.editorBackgroundColor,
borderRadius: BorderRadius.circular(25),
border: isSelected
? Border.all(color: Colors.black, width: 2)

View File

@@ -1,6 +1,7 @@
import 'dart:math';
import 'package:flutter/material.dart';
import "package:flutter/services.dart";
import "package:flutter_svg/svg.dart";
import "package:photos/ente_theme_data.dart";
import "package:photos/theme/ente_theme.dart";
@@ -36,6 +37,24 @@ class _ImageEditorTuneBarState extends State<ImageEditorTuneBar>
with ImageEditorConvertedConfigs, SimpleConfigsAccessState {
TuneEditorState get tuneEditor => widget.editor;
final Map<int, double> _lastValues = {};
void _handleTuneItemTap(int index) {
if (tuneEditor.selectedIndex == index) {
final currentValue = tuneEditor.tuneAdjustmentMatrix[index].value;
if (currentValue != 0) {
_lastValues[index] = currentValue;
tuneEditor.onChanged(0);
} else if (_lastValues.containsKey(index)) {
tuneEditor.onChanged(_lastValues[index]!);
}
} else {
tuneEditor.setState(() {
tuneEditor.selectedIndex = index;
});
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
@@ -77,11 +96,7 @@ class _ImageEditorTuneBarState extends State<ImageEditorTuneBar>
value: tuneEditor.tuneAdjustmentMatrix[index].value,
max: item.max,
min: item.min,
onTap: () {
tuneEditor.setState(() {
tuneEditor.selectedIndex = index;
});
},
onTap: () => _handleTuneItemTap(index),
);
}),
),
@@ -224,7 +239,10 @@ class _CircularProgressWithValueState extends State<CircularProgressWithValue>
@override
void didUpdateWidget(CircularProgressWithValue oldWidget) {
super.didUpdateWidget(oldWidget);
if ((oldWidget.value < 0 && widget.value >= 0) ||
(oldWidget.value > 0 && widget.value <= 0)) {
HapticFeedback.vibrate();
}
if (oldWidget.value != widget.value) {
_previousValue = oldWidget.value;
_progressAnimation = Tween<double>(
@@ -303,11 +321,11 @@ class _CircularProgressWithValueState extends State<CircularProgressWithValue>
shape: BoxShape.circle,
color: showValue || widget.isSelected
? progressColor.withOpacity(0.2)
: colorTheme.backgroundElevated2,
: Theme.of(context).colorScheme.editorBackgroundColor,
border: Border.all(
color: widget.isSelected
? progressColor.withOpacity(0.4)
: colorTheme.backgroundElevated2,
: Theme.of(context).colorScheme.editorBackgroundColor,
width: 2,
),
),
@@ -395,7 +413,7 @@ class _TuneAdjustWidget extends StatelessWidget {
margin: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: colorScheme.backgroundElevated2,
color: Theme.of(context).colorScheme.editorBackgroundColor,
),
),
),
@@ -410,7 +428,8 @@ class _TuneAdjustWidget extends StatelessWidget {
overlayShape: const RoundSliderOverlayShape(overlayRadius: 0),
activeTrackColor:
Theme.of(context).colorScheme.imageEditorPrimaryColor,
inactiveTrackColor: colorScheme.backgroundElevated2,
inactiveTrackColor:
Theme.of(context).colorScheme.editorBackgroundColor,
trackShape: const _CenterBasedTrackShape(),
trackHeight: 24,
),

View File

@@ -31,7 +31,7 @@ class VideoEditorBottomAction extends StatelessWidget {
height: 48,
width: 48,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.videoPlayerBackgroundColor,
color: Theme.of(context).colorScheme.editorBackgroundColor,
shape: BoxShape.circle,
border: Border.all(
color: isSelected

View File

@@ -43,7 +43,7 @@ class VideoEditorPlayerControl extends StatelessWidget {
vertical: 4,
),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.videoPlayerBackgroundColor,
color: Theme.of(context).colorScheme.editorBackgroundColor,
borderRadius: BorderRadius.circular(56),
),
child: Row(

View File

@@ -74,7 +74,7 @@ class _VideoEditorPageState extends State<VideoEditorPage> {
trimStyle: TrimSliderStyle(
onTrimmedColor: const ColorScheme.dark().videoPlayerPrimaryColor,
onTrimmingColor: const ColorScheme.dark().videoPlayerPrimaryColor,
background: Theme.of(context).colorScheme.videoPlayerBackgroundColor,
background: Theme.of(context).colorScheme.editorBackgroundColor,
positionLineColor:
Theme.of(context).colorScheme.videoPlayerBorderColor,
lineColor: Theme.of(context)

View File

@@ -1 +1,2 @@
- Aman: Fixed bottom nav bar color in light theme, resolved paint editor's initial color, and added tap-to-reset with haptics for tune adjustments (brightness/exposure)
- Gracefully handle heic rendering on Android