manu changes and added setting

This commit is contained in:
Naser Elziadna
2024-12-08 15:00:33 +02:00
parent 4781164e0b
commit 23674991a2
53 changed files with 726 additions and 85 deletions

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#121212</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -5,6 +5,10 @@
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#ffffff</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -5,6 +5,10 @@
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@@ -1,4 +0,0 @@
flutter_icons:
android: "launcher_icon"
ios: true
image_path: "assets/launcher_icon.png"

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 69 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 325 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 674 KiB

View File

@@ -38,7 +38,7 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="512" height="512"/>
<image name="LaunchImage" width="480" height="480"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>

View File

@@ -0,0 +1,21 @@
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:doddle/domain/models/effects/settings/brush_settings_state.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final brushSettingsProvider = StateNotifierProvider.family<BrushSettingsNotifier, BrushSettingsState, PenTool>((ref, penTool) {
return BrushSettingsNotifier(penTool);
});
class BrushSettingsNotifier extends StateNotifier<BrushSettingsState> {
BrushSettingsNotifier(PenTool penTool) : super(BrushSettingsState(penTool: penTool));
void updateSetting(String key, dynamic value) {
state = state.copyWith(
values: {...state.values, key: value},
);
}
void resetToDefault() {
state = BrushSettingsState(penTool: state.penTool);
}
}

View File

@@ -1,21 +1,55 @@
import 'dart:ui';
import 'package:doddle/application/providers/brush_settings_provider.dart';
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:doddle/domain/models/effects/pen_effect.dart';
import 'package:doddle/domain/models/effects/settings/brush_settings_state.dart';
import 'package:doddle/main.dart';
import 'package:flutter/material.dart';
class GlowEffect extends PenEffect {
BrushSettingsState get settings => globalRef.read(brushSettingsProvider(PenTool.glowPen));
double get intensity => settings.getValue('intensity') ?? 0.5;
double get blur => settings.getValue('blur') ?? 3.0;
double get outerGlow => settings.getValue('outerGlow') ?? 5.0;
bool get innerGlow => settings.getValue('innerGlow') ?? true;
bool get rainbow => settings.getValue('rainbow') ?? false;
@override
void paint(Canvas canvas, Path path, Paint paint) {
// Draw outer glow
if (outerGlow > 0) {
canvas.drawPath(
path,
Paint()
..maskFilter = MaskFilter.blur(BlurStyle.normal, blur)
..color = rainbow ? _getRainbowColor() : drawController.currentColor.withOpacity(intensity)
..style = PaintingStyle.stroke
..strokeWidth = drawController.penSize! + outerGlow,
);
}
// Draw inner glow if enabled
if (innerGlow) {
canvas.drawPath(
path,
Paint()
..maskFilter = MaskFilter.blur(BlurStyle.inner, blur * 0.5)
..color = rainbow ? _getRainbowColor() : drawController.currentColor.withOpacity(intensity * 0.7)
..style = PaintingStyle.stroke
..strokeWidth = drawController.penSize!,
);
}
// Draw the main stroke
canvas.drawPath(
path,
Paint()
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 3.0)
..color = drawController.currentColor
..style = PaintingStyle.stroke
..strokeWidth = drawController.penSize! + 5,
paint
..color = Colors.white
..strokeWidth = drawController.penSize! * 0.8,
);
}
canvas.drawPath(path, paint..color = Colors.white);
Color _getRainbowColor() {
final hue = (DateTime.now().millisecondsSinceEpoch / 50) % 360;
return HSVColor.fromAHSV(1.0, hue, 1.0, 1.0).toColor();
}
}

View File

@@ -1,27 +1,37 @@
import 'package:doddle/application/providers/brush_settings_provider.dart';
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:doddle/domain/models/effects/pen_effect.dart';
import 'package:doddle/domain/models/effects/settings/brush_settings_state.dart';
import 'package:doddle/main.dart';
import 'package:flutter/material.dart';
import 'package:path_drawing/path_drawing.dart';
class GlowWithDotsEffect extends PenEffect {
BrushSettingsState get settings =>
globalRef.read(brushSettingsProvider(PenTool.glowWithDotsPen));
double get dashLength => settings.getValue('dashLength') ?? 5.0;
double get gapLength => settings.getValue('gapLength') ?? 10.0;
double get glowRadius => settings.getValue('glowRadius') ?? 5.0;
@override
void paint(Canvas canvas, Path path, Paint paint) {
final pathWithDots = dashPath(
path,
dashArray: CircularIntervalList<double>(<double>[5
, 10]), // Changed to create 5px dashes with 10px gaps
);
final pathWithDots = dashPath(
path,
dashArray: CircularIntervalList<double>(<double>[dashLength, gapLength]),
);
canvas.drawPath(
pathWithDots,
Paint()
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5.0)
..maskFilter = MaskFilter.blur(BlurStyle.normal, glowRadius)
..color = drawController.currentColor
..style = PaintingStyle.stroke
..strokeWidth = 5.0);
..strokeWidth = drawController.penSize!);
canvas.drawPath(
pathWithDots,
paint..strokeWidth = drawController.penSize!);
paint
..strokeWidth = drawController.penSize!
..color = Colors.white);
}
}

View File

@@ -11,12 +11,8 @@ import 'package:doddle/application/providers/canvas/canvas_provider.dart';
abstract class PenEffect {
DrawController get drawController => globalRef.read(canvasNotifierProvider);
// void initialize(Ref ref) {
// drawController = ref.read(canvasNotifierProvider);
// }
void paint(Canvas canvas, Path path, Paint paint);
// New method to handle point additions

View File

@@ -0,0 +1,157 @@
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:flutter/material.dart';
enum SettingType {
slider,
toggle,
color,
// Add more types as needed
}
class BrushSettingConfig {
final String label;
final SettingType type;
final dynamic defaultValue;
final dynamic minValue;
final dynamic maxValue;
final int? divisions;
final IconData? icon;
const BrushSettingConfig({
required this.label,
required this.type,
required this.defaultValue,
this.minValue,
this.maxValue,
this.divisions,
this.icon,
});
}
class BrushConfig {
final Map<String, BrushSettingConfig> settings;
final String name;
final String description;
const BrushConfig({
required this.name,
required this.settings,
this.description = '',
});
}
// Define configurations for each brush type
class BrushConfigs {
static final Map<PenTool, BrushConfig> configs = {
PenTool.normalPen: BrushConfig(
name: 'Normal Brush',
settings: {}, // Normal pen has no special settings, just uses the base color and size
),
PenTool.glowWithDotsPen: BrushConfig(
name: 'Glow with Dots Brush',
settings: {
'dashLength': BrushSettingConfig(
label: 'Dash Length',
type: SettingType.slider,
defaultValue: 5.0,
minValue: 1.0,
maxValue: 20.0,
icon: Icons.horizontal_rule,
),
'gapLength': BrushSettingConfig(
label: 'Gap Length',
type: SettingType.slider,
defaultValue: 10.0,
minValue: 1.0,
maxValue: 30.0,
icon: Icons.space_bar,
),
'glowRadius': BrushSettingConfig(
label: 'Glow Radius',
type: SettingType.slider,
defaultValue: 5.0,
minValue: 0.0,
maxValue: 20.0,
icon: Icons.blur_on,
),
},
),
PenTool.sprayPen: BrushConfig(
name: 'Spray Brush',
settings: {
'density': BrushSettingConfig(
label: 'Density',
type: SettingType.slider,
defaultValue: 10.0,
minValue: 1.0,
maxValue: 30.0,
icon: Icons.grain,
),
'spread': BrushSettingConfig(
label: 'Spread',
type: SettingType.slider,
defaultValue: 10.0,
minValue: 1.0,
maxValue: 50.0,
icon: Icons.radio_button_unchecked,
),
'opacity': BrushSettingConfig(
label: 'Opacity',
type: SettingType.slider,
defaultValue: 0.3,
minValue: 0.1,
maxValue: 1.0,
icon: Icons.opacity,
),
'randomizeEachDot': BrushSettingConfig(
label: 'Randomize Each Dot',
type: SettingType.toggle,
defaultValue: false,
icon: Icons.color_lens,
),
},
),
PenTool.glowPen: BrushConfig(
name: 'Glow Brush',
settings: {
'intensity': BrushSettingConfig(
label: 'Glow Intensity',
type: SettingType.slider,
defaultValue: 0.5,
minValue: 0.1,
maxValue: 1.0,
icon: Icons.brightness_medium,
),
'blur': BrushSettingConfig(
label: 'Blur Amount',
type: SettingType.slider,
defaultValue: 3.0,
minValue: 1.0,
maxValue: 10.0,
icon: Icons.blur_on,
),
'outerGlow': BrushSettingConfig(
label: 'Outer Glow Size',
type: SettingType.slider,
defaultValue: 5.0,
minValue: 0.0,
maxValue: 20.0,
icon: Icons.radio_button_unchecked,
),
'innerGlow': BrushSettingConfig(
label: 'Inner Glow',
type: SettingType.toggle,
defaultValue: true,
icon: Icons.circle,
),
'rainbow': BrushSettingConfig(
label: 'Rainbow Mode',
type: SettingType.toggle,
defaultValue: false,
icon: Icons.color_lens,
),
},
),
// Add more brush configurations...
};
}

View File

@@ -0,0 +1,34 @@
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:doddle/domain/models/effects/settings/brush_settings_config.dart';
class BrushSettingsState {
final Map<String, dynamic> values;
final PenTool penTool;
BrushSettingsState({
required this.penTool,
Map<String, dynamic>? values,
}) : values = values ?? _getDefaultValues(penTool);
static Map<String, dynamic> _getDefaultValues(PenTool penTool) {
final config = BrushConfigs.configs[penTool];
if (config == null) return {};
return Map.fromEntries(
config.settings.entries.map(
(e) => MapEntry(e.key, e.value.defaultValue),
),
);
}
BrushSettingsState copyWith({
Map<String, dynamic>? values,
}) {
return BrushSettingsState(
penTool: penTool,
values: values ?? this.values,
);
}
dynamic getValue(String key) => values[key];
}

View File

@@ -1,6 +1,9 @@
import 'dart:ui';
import 'package:doddle/application/providers/brush_settings_provider.dart';
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:doddle/domain/models/effects/settings/brush_settings_state.dart';
import 'package:doddle/domain/models/point.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:doddle/main.dart';
import 'package:doddle/domain/models/effects/pen_effect.dart';
import 'dart:math' as math;
@@ -21,21 +24,25 @@ class SprayDot {
class SprayEffect extends PenEffect {
final random = math.Random();
List<SprayDot> sprayDots = [];
BrushSettingsState get settings => globalRef.read(brushSettingsProvider(PenTool.sprayPen));
double get density => settings.getValue('density') ?? 10;
double get spread => settings.getValue('spread') ?? 10;
double get opacity => settings.getValue('opacity') ?? 0.3;
bool get randomizeEachDot => settings.getValue('randomizeEachDot') ?? false;
@override
void paint(Canvas canvas, Path path, Paint paint) {
print('density: $density, spread: $spread, opacity: $opacity');
for (var dot in sprayDots) {
// Get all symmetrical positions for this dot
final symmetricalDots = getSymmetricalPositions(dot.position);
// Draw a dot at each symmetrical position
for (var position in symmetricalDots) {
canvas.drawCircle(
position,
dot.size * (drawController.penSize ?? 2.0),
paint
..color = dot.color.withOpacity(dot.opacity)
);
canvas.drawCircle(position, dot.size * (drawController.penSize ?? 2.0),
paint..color = dot.color.withOpacity(dot.opacity));
}
}
}
@@ -43,22 +50,24 @@ class SprayEffect extends PenEffect {
@override
void onPointAdd(Point point) {
if (point.offset == null) return;
// Create multiple spray dots around the point
final numDots = 10; // Adjust this for more/less density
final numDots = density.toInt();
for (int i = 0; i < numDots; i++) {
final spread = 10.0; // Adjust this for wider/narrower spray
final randomOffset = Offset(
random.nextDouble() * spread - spread/2,
random.nextDouble() * spread - spread/2
);
final randomOffset = Offset(random.nextDouble() * spread - spread / 2,
random.nextDouble() * spread - spread / 2);
final color = randomizeEachDot
? getRandomColor()
: (drawController.isRandomColor
? getRandomColor()
: drawController.currentColor);
addSprayDots([
SprayDot(
position: point.offset! + randomOffset,
opacity: random.nextDouble() * 0.3 + 0.1, // Random opacity between 0.1 and 0.4
size: random.nextDouble() * 0.5 + 0.5, // Random size between 0.5 and 1.0
color: drawController.isRandomColor ? getRandomColor() : drawController.currentColor,
opacity: random.nextDouble() * opacity,
size: random.nextDouble() * 0.5 + 0.5,
color: color,
),
]);
}

View File

@@ -0,0 +1,135 @@
import 'package:doddle/application/providers/canvas/canvas_provider.dart';
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:doddle/domain/models/effects/settings/brush_settings_config.dart';
import 'package:doddle/application/providers/brush_settings_provider.dart';
import 'package:doddle/presentation/common/widgets/brush_settings/brush_viewer.dart';
class BrushSettingsPanel extends ConsumerWidget {
const BrushSettingsPanel({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentPenTool = ref.watch(canvasNotifierProvider).penTool;
if (currentPenTool == null) return const SizedBox.shrink();
final brushConfig = BrushConfigs.configs[currentPenTool];
if (brushConfig == null) return const SizedBox.shrink();
final settings = ref.watch(brushSettingsProvider(currentPenTool));
return Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
brushConfig.name,
style: Theme.of(context).textTheme.titleLarge,
),
if (brushConfig.description.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
brushConfig.description,
style: Theme.of(context).textTheme.bodyMedium,
),
],
const SizedBox(height: 16),
const BrushPreview(),
const SizedBox(height: 16),
...brushConfig.settings.entries.map((entry) {
return _buildSettingControl(
context,
ref,
currentPenTool,
entry.key,
entry.value,
settings.getValue(entry.key),
);
}),
const SizedBox(height: 16),
_buildResetButton(context, ref, currentPenTool),
],
),
);
}
Widget _buildSettingControl(
BuildContext context,
WidgetRef ref,
PenTool penTool,
String key,
BrushSettingConfig config,
dynamic currentValue,
) {
switch (config.type) {
case SettingType.slider:
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
if (config.icon != null) ...[
Icon(config.icon, size: 20),
const SizedBox(width: 8),
],
Text(config.label),
const Spacer(),
Text(currentValue.toStringAsFixed(1)),
],
),
Slider(
value: currentValue,
min: config.minValue,
max: config.maxValue,
divisions: config.divisions,
onChanged: (value) {
ref.read(brushSettingsProvider(penTool).notifier)
.updateSetting(key, value);
},
),
],
),
);
case SettingType.toggle:
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
if (config.icon != null) ...[
Icon(config.icon, size: 20),
const SizedBox(width: 8),
],
Text(config.label),
const Spacer(),
Switch.adaptive(
value: currentValue ?? false,
onChanged: (value) {
ref.read(brushSettingsProvider(penTool).notifier)
.updateSetting(key, value);
},
),
],
),
);
default:
return const SizedBox.shrink();
}
}
Widget _buildResetButton(BuildContext context, WidgetRef ref, PenTool penTool) {
return Center(
child: TextButton.icon(
icon: const Icon(Icons.restart_alt),
label: const Text('Reset to Default'),
onPressed: () {
ref.read(brushSettingsProvider(penTool).notifier).resetToDefault();
},
),
);
}
}

View File

@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:doddle/presentation/painting/brush_preview_painter.dart';
import 'package:sizer/sizer.dart';
class BrushPreview extends ConsumerWidget {
const BrushPreview({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Container(
height: 150.h,
width: double.infinity,
margin: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(8),
),
child: CustomPaint(
painter: BrushPreviewPainter(ref),
),
);
}
}

View File

@@ -35,7 +35,7 @@ class BrushToolGrid extends ConsumerWidget {
child: GestureDetector(
onTap: () {
ref.read(canvasNotifierProvider.notifier).changePenTool(brush.penTool);
Navigator.of(context).pop();
// Navigator.of(context).pop();
},
child: Container(
decoration: BoxDecoration(

View File

@@ -10,6 +10,7 @@ import 'brush_tool_grid.dart';
import 'color_tool_grid.dart';
import 'symmetry_tool_grid.dart';
import 'canvas_settings_tool_grid.dart';
import 'package:doddle/presentation/common/widgets/brush_settings/brush_settings_panel.dart';
class ToolsWidget extends ConsumerWidget {
const ToolsWidget({Key? key}) : super(key: key);
@@ -84,7 +85,13 @@ class ToolsWidget extends ConsumerWidget {
required ToolType toolType,
}) {
return GestureDetector(
onTap: () => _showToolSettings(context, toolType),
onTap: () {
if (toolType == ToolType.brushs) {
_showBrushSettings(context);
} else {
_showToolSettings(context, toolType);
}
},
child: icon,
);
}
@@ -110,6 +117,24 @@ class ToolsWidget extends ConsumerWidget {
);
}
void _showBrushSettings(BuildContext context) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return const SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
BrushToolGrid(),
Divider(),
BrushSettingsPanel(),
],
),
);
},
);
}
Widget _buildToolContent(ToolType toolType) {
switch (toolType) {
case ToolType.brushs:

View File

@@ -0,0 +1,75 @@
import 'package:doddle/application/providers/canvas/canvas_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:doddle/domain/models/draw_controller.dart';
import 'package:doddle/domain/models/point.dart';
import 'dart:math' as math;
class BrushPreviewPainter extends CustomPainter {
final WidgetRef ref;
BrushPreviewPainter(this.ref);
@override
void paint(Canvas canvas, Size size) {
canvas.save();
final center = Offset(size.width / 2, size.height / 2);
canvas.translate(center.dx, center.dy);
// Create a wavy line for preview
final points = _generatePreviewPoints(size);
final controller = DrawController(
points: points,
penTool: ref.read(canvasNotifierProvider).penTool,
penSize: ref.read(canvasNotifierProvider).penSize,
currentColor: ref.read(canvasNotifierProvider).currentColor,
effects: ref.read(canvasNotifierProvider).effects,
);
final effect = controller.effects[controller.penTool];
if (effect != null) {
Path path = Path();
Paint paint = Paint()
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
_drawPreviewPoints(canvas, path, paint, effect, points);
effect.paint(canvas, path, paint);
}
canvas.restore();
}
List<Point?> _generatePreviewPoints(Size size) {
final points = <Point?>[];
final width = size.width * 0.4;
for (double x = -width/2; x <= width/2; x += 60) {
final y = math.sin(x * 0.1) * 20;
points.add(Point(offset: Offset(x, y)));
}
return points;
}
void _drawPreviewPoints(Canvas canvas, Path path, Paint paint, dynamic effect, List<Point?> points) {
for (var j = 0; j < points.length - 1; j++) {
final currentPoint = points[j]?.offset;
final nextPoint = points[j + 1]?.offset;
if (currentPoint != null && nextPoint != null) {
final currentSymPoints = effect.getSymmetricalPositions(currentPoint);
final nextSymPoints = effect.getSymmetricalPositions(nextPoint);
for (var i = 0; i < currentSymPoints.length; i++) {
path.moveTo(currentSymPoints[i].dx, currentSymPoints[i].dy);
path.lineTo(nextSymPoints[i].dx, nextSymPoints[i].dy);
}
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

View File

@@ -10,7 +10,8 @@ class LastImageAsBackground extends CustomPainter {
void paint(Canvas canvas, Size size) {
if (image != null) {
canvas.drawImage(image!, Offset.zero, ui.Paint()
// ..filterQuality = ui.FilterQuality.high
..blendMode = BlendMode.srcIn
..filterQuality = ui.FilterQuality.high
// ..isAntiAlias = true,
);
}

View File

@@ -487,10 +487,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77"
sha256: "31cd0885738e87c72d6f055564d37fabcdacee743b396b78c7636c169cac64f5"
url: "https://pub.dev"
source: hosted
version: "0.14.1"
version: "0.14.2"
flutter_lints:
dependency: "direct dev"
description:

View File

@@ -54,7 +54,7 @@ dependencies:
firebase_database: ^11.1.6
# google_mobile_ads: ^5.2.0
screen_recorder: ^0.3.0
flutter_native_splash: ^2.2.6
flutter_native_splash: ^2.4.3
sizer: ^3.0.5
shared_preferences: ^2.3.3
go_router: ^14.6.1
@@ -64,6 +64,16 @@ dependencies:
flutter_native_splash:
color: "#000000"
image: assets/animation_logo.gif
android_12:
image: assets/animation_logo.gif
icon_background_color: "#ffffff"
image_dark: assets/animation_logo.gif
icon_background_color_dark: "#121212"
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "assets/launcher_icon.png"
dev_dependencies:
flutter_test:
@@ -76,7 +86,7 @@ dev_dependencies:
# rules and activating additional ones.
build_runner: null
flutter_lints: ^5.0.0
flutter_launcher_icons: ^0.14.1
flutter_launcher_icons: ^0.14.2
flutter_gen_runner: ^5.8.0
riverpod_generator: ^2.6.1

View File

@@ -1,6 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<!DOCTYPE html><html><head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
@@ -27,20 +25,89 @@
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="icon" type="image/png" href="favicon.png">
<title>doddle</title>
<link rel="manifest" href="manifest.json">
<script src="splash/splash.js"></script>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<link rel="stylesheet" type="text/css" href="splash/style.css">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<style id="splash-screen-style">
html {
height: 100%
}
body {
margin: 0;
min-height: 100%;
background-color: #000000;
background-size: 100% 100%;
}
.center {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.contain {
display:block;
width:100%; height:100%;
object-fit: contain;
}
.stretch {
display:block;
width:100%; height:100%;
}
.cover {
display:block;
width:100%; height:100%;
object-fit: cover;
}
.bottom {
position: absolute;
bottom: 0;
left: 50%;
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.bottomLeft {
position: absolute;
bottom: 0;
left: 0;
}
.bottomRight {
position: absolute;
bottom: 0;
right: 0;
}
</style>
<script id="splash-screen-script">
function removeSplashFromWeb() {
document.getElementById("splash")?.remove();
document.getElementById("splash-branding")?.remove();
document.body.style.background = "transparent";
}
</script>
</head>
<body>
<picture id="splash">
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt=""/>
<source srcset="splash/img/light-1x.gif 1x, splash/img/light-2x.gif 2x, splash/img/light-3x.gif 3x, splash/img/light-4x.gif 4x" media="(prefers-color-scheme: light)">
<source srcset="splash/img/dark-1x.gif 1x, splash/img/dark-2x.gif 2x, splash/img/dark-3x.gif 3x, splash/img/dark-4x.gif 4x" media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.gif" alt="">
</picture>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
@@ -108,5 +175,6 @@
loadMainDartJs();
}
</script>
</body>
</html>
</body></html>

BIN
web/splash/img/dark-1x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
web/splash/img/dark-2x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

BIN
web/splash/img/dark-3x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 KiB

BIN
web/splash/img/dark-4x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
web/splash/img/light-1x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
web/splash/img/light-2x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

BIN
web/splash/img/light-3x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 KiB

BIN
web/splash/img/light-4x.gif Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB