Skip to content

Commit

Permalink
PAINTROID-454: Flutter: Add Layers - reorder positions, fix some bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
Lenkomotive committed Oct 12, 2024
1 parent 9d428d8 commit 3dc0ed4
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 197 deletions.
41 changes: 28 additions & 13 deletions lib/core/providers/state/layer_menu_state_provider.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import 'package:flutter/cupertino.dart';
import 'package:paintroid/core/providers/state/layer_menu_state_data.dart';
import 'package:paintroid/core/providers/state/layer_state_data.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:uuid/uuid.dart';

part 'layer_menu_state_provider.g.dart';

@Riverpod(keepAlive: true)
class LayerMenuStateProvider extends _$LayerMenuStateProvider {
final uuid = const Uuid();

@override
LayerMenuStateData build() {
return const LayerMenuStateData(
return LayerMenuStateData(
isVisible: false,
layer: [
LayerStateData(id: 0, isSelected: false),
LayerStateData(id: 1, isSelected: false),
LayerStateData(id: 2, isSelected: false),
LayerStateData(id: 3, isSelected: false),
LayerStateData(id: 4, isSelected: false),
LayerStateData(id: 5, isSelected: false),
LayerStateData(id: 6, isSelected: false),
LayerStateData(
key: ValueKey(uuid.v4()),
isSelected: false,
),
],
);
}
Expand All @@ -37,19 +38,33 @@ class LayerMenuStateProvider extends _$LayerMenuStateProvider {
state = state.copyWith(layer: layerList);
}

void toggleSelection(int layerId) {
final updatedLayerList = state.layer.map((layer) {
if (layer.id == layerId) {
return layer.copyWith(isSelected: !layer.isSelected);
void toggleSelection(Key? layerKey) {
final layers = state.layer;

final selectedCount = layers.where((layer) => layer.isSelected).length;
final unselectedCount = layers.length - selectedCount;

final updatedLayerList = layers.map((layer) {
if (layer.key == layerKey) {
if (!layer.isSelected) {
if (unselectedCount <= 1) {
return layer;
} else {
return layer.copyWith(isSelected: true);
}
} else {
return layer.copyWith(isSelected: false);
}
}
return layer;
}).toList();

state = state.copyWith(layer: updatedLayerList);
}

void addLayer() {
final newLayer = LayerStateData(
id: state.layer.length + 1,
key: ValueKey(uuid.v4()),
isSelected: false,
);
state = state.copyWith(layer: [...state.layer, newLayer]);
Expand Down
2 changes: 1 addition & 1 deletion lib/core/providers/state/layer_menu_state_provider.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion lib/core/providers/state/layer_state_data.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'layer_state_data.freezed.dart';
Expand All @@ -6,7 +7,7 @@ part 'layer_state_data.freezed.dart';
@freezed
class LayerStateData with _$LayerStateData {
const factory LayerStateData({
required int id,
required ValueKey key,
required bool isSelected,
}) = _LayerStateData;
}
40 changes: 20 additions & 20 deletions lib/core/providers/state/layer_state_data.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final _privateConstructorUsedError = UnsupportedError(

/// @nodoc
mixin _$LayerStateData {
int get id => throw _privateConstructorUsedError;
ValueKey<dynamic> get key => throw _privateConstructorUsedError;
bool get isSelected => throw _privateConstructorUsedError;

@JsonKey(ignore: true)
Expand All @@ -30,7 +30,7 @@ abstract class $LayerStateDataCopyWith<$Res> {
LayerStateData value, $Res Function(LayerStateData) then) =
_$LayerStateDataCopyWithImpl<$Res, LayerStateData>;
@useResult
$Res call({int id, bool isSelected});
$Res call({ValueKey<dynamic> key, bool isSelected});
}

/// @nodoc
Expand All @@ -46,14 +46,14 @@ class _$LayerStateDataCopyWithImpl<$Res, $Val extends LayerStateData>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? key = null,
Object? isSelected = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
key: null == key
? _value.key
: key // ignore: cast_nullable_to_non_nullable
as ValueKey<dynamic>,
isSelected: null == isSelected
? _value.isSelected
: isSelected // ignore: cast_nullable_to_non_nullable
Expand All @@ -70,7 +70,7 @@ abstract class _$$LayerStateDataImplCopyWith<$Res>
__$$LayerStateDataImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int id, bool isSelected});
$Res call({ValueKey<dynamic> key, bool isSelected});
}

/// @nodoc
Expand All @@ -84,14 +84,14 @@ class __$$LayerStateDataImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? key = null,
Object? isSelected = null,
}) {
return _then(_$LayerStateDataImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
key: null == key
? _value.key
: key // ignore: cast_nullable_to_non_nullable
as ValueKey<dynamic>,
isSelected: null == isSelected
? _value.isSelected
: isSelected // ignore: cast_nullable_to_non_nullable
Expand All @@ -103,30 +103,30 @@ class __$$LayerStateDataImplCopyWithImpl<$Res>
/// @nodoc
class _$LayerStateDataImpl implements _LayerStateData {
const _$LayerStateDataImpl({required this.id, required this.isSelected});
const _$LayerStateDataImpl({required this.key, required this.isSelected});

@override
final int id;
final ValueKey<dynamic> key;
@override
final bool isSelected;

@override
String toString() {
return 'LayerStateData(id: $id, isSelected: $isSelected)';
return 'LayerStateData(key: $key, isSelected: $isSelected)';
}

@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LayerStateDataImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.key, key) || other.key == key) &&
(identical(other.isSelected, isSelected) ||
other.isSelected == isSelected));
}

@override
int get hashCode => Object.hash(runtimeType, id, isSelected);
int get hashCode => Object.hash(runtimeType, key, isSelected);

@JsonKey(ignore: true)
@override
Expand All @@ -138,11 +138,11 @@ class _$LayerStateDataImpl implements _LayerStateData {

abstract class _LayerStateData implements LayerStateData {
const factory _LayerStateData(
{required final int id,
{required final ValueKey<dynamic> key,
required final bool isSelected}) = _$LayerStateDataImpl;

@override
int get id;
ValueKey<dynamic> get key;
@override
bool get isSelected;
@override
Expand Down
77 changes: 54 additions & 23 deletions lib/ui/pages/workspace_page/components/layer_menu/layer.dart
Original file line number Diff line number Diff line change
@@ -1,48 +1,79 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:paintroid/core/providers/state/layer_menu_state_provider.dart';
import 'package:paintroid/ui/theme/data/custom_colors.dart';

List<Color> customColors = [
const Color(0xFFFF5733), // Fiery Red
const Color(0xFF33C1FF), // Sky Blue
const Color(0xFF75FF33), // Lime Green
const Color(0xFFFF33A8), // Hot Pink
const Color(0xFF33FFF5), // Aqua
const Color(0xFFFFD133), // Golden Yellow
const Color(0xFF8D33FF), // Deep Purple
const Color(0xFFFF8333), // Vibrant Orange
const Color(0xFF33FF8D), // Mint Green
const Color(0xFF3361FF), // Bright Indigo
];
const layerHeight = 180.0;

class Layer extends ConsumerWidget {
const Layer({
super.key,
this.isSelected = false,
required this.id,
});

final int id;
final bool isSelected;

@override
Widget build(BuildContext context, WidgetRef ref) {
return GestureDetector(
onTap: () {
ref.read(layerMenuStateProvider.notifier).toggleSelection(id);
ref.read(layerMenuStateProvider.notifier).toggleSelection(key);
},
child: Container(
height: 80,
height: 180,
margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: customColors[id],
color: CustomColors.oceanBlue.withOpacity(isSelected ? 0.5 : 1),
borderRadius: BorderRadius.circular(12),
border: isSelected
? Border.all(
color: Colors.blue,
width: 5.0,
)
: null,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Column(
children: [
IconButton(
icon: const Icon(Icons.visibility),
color: Colors.white,
onPressed: () {},
),
Expanded(
child: RotatedBox(
quarterTurns: 3,
child: Slider(
value: 0.5,
activeColor: Colors.grey,
inactiveColor: CustomColors.lightGray,
onChanged: (value) {},
),
),
)
],
),
),
// const Spacer(),
Container(
width: 80,
height: 180 - 32,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Text(
key.toString(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
IconButton(
icon: const Icon(Icons.drag_indicator_outlined),
color: Colors.white,
onPressed: () {},
),
],
),
),
);
Expand Down
24 changes: 16 additions & 8 deletions lib/ui/pages/workspace_page/components/layer_menu/layer_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,22 @@ class LayerMenu extends ConsumerWidget {
final layers = ref.watch(
layerMenuStateProvider.select((state) => state.layer),
);
return Positioned(
top: 54,
bottom: 54,
right: 8,

return Align(
alignment: Alignment.centerRight,
child: FadeInOutWidget(
isVisible: isVisible,
child: Container(
width: 200,
constraints: BoxConstraints(
maxHeight: _calculateHeight(layers.length, context),
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.7),
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
Expand All @@ -46,9 +49,9 @@ class LayerMenu extends ConsumerWidget {
),
],
),
Expanded(
Flexible(
child: ReorderableListView(
padding: const EdgeInsets.all(8),
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
onReorder: (int oldIndex, int newIndex) {
ref.read(layerMenuStateProvider.notifier).reorder(
oldIndex,
Expand All @@ -57,8 +60,7 @@ class LayerMenu extends ConsumerWidget {
},
children: List.generate(layers.length, (index) {
return Layer(
id: layers[index].id,
key: ValueKey(layers[index].id),
key: layers[index].key,
isSelected: layers[index].isSelected,
);
}),
Expand All @@ -70,4 +72,10 @@ class LayerMenu extends ConsumerWidget {
),
);
}

double _calculateHeight(int layerCount, BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
final double maxHeight = screenHeight * 0.66;
return (layerCount * layerHeight + 64).clamp(0.0, maxHeight);
}
}
Loading

0 comments on commit 3dc0ed4

Please sign in to comment.