From 25f673b47ddce3be1772af6a93e1989df9fbd69e Mon Sep 17 00:00:00 2001 From: Stefan Knipfer Date: Fri, 3 Jul 2026 11:02:21 +0200 Subject: [PATCH 1/2] [PAINTROID-809] Fix hidden help menu buttons --- .../components/bottom_nav_bar_container.dart | 13 +- .../onboarding_page_bottom_nav_bar.dart | 78 +++++++--- .../onboarding_page/onboarding_page.dart | 144 +++++++++--------- .../onboarding_page/screens/screen2.dart | 56 +++---- .../onboarding_page/screens/screen3.dart | 84 ++++------ 5 files changed, 186 insertions(+), 189 deletions(-) diff --git a/lib/ui/pages/onboarding_page/components/bottom_nav_bar_container.dart b/lib/ui/pages/onboarding_page/components/bottom_nav_bar_container.dart index 7dfa10e6..3225ead3 100644 --- a/lib/ui/pages/onboarding_page/components/bottom_nav_bar_container.dart +++ b/lib/ui/pages/onboarding_page/components/bottom_nav_bar_container.dart @@ -4,24 +4,21 @@ import 'package:paintroid/ui/pages/onboarding_page/components/onboarding_page_bo import 'package:paintroid/ui/theme/theme.dart'; class BottomNavigationBarContainer extends StatelessWidget { - final List navBarItems; - final List onPressedFunctions; + final List items; const BottomNavigationBarContainer({ super.key, - required this.navBarItems, - required this.onPressedFunctions, + required this.items, }); @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.symmetric(vertical: 10), + padding: const EdgeInsets.symmetric(vertical: 20), color: PaintroidTheme.of(context).surfaceColor, child: OnboardingPageBottomNavigationBar( - onPressedFunctions: onPressedFunctions, - barItems: navBarItems, + items: items, ), ); } -} +} \ No newline at end of file diff --git a/lib/ui/pages/onboarding_page/components/onboarding_page_bottom_nav_bar.dart b/lib/ui/pages/onboarding_page/components/onboarding_page_bottom_nav_bar.dart index d027aa77..c01fa589 100644 --- a/lib/ui/pages/onboarding_page/components/onboarding_page_bottom_nav_bar.dart +++ b/lib/ui/pages/onboarding_page/components/onboarding_page_bottom_nav_bar.dart @@ -3,37 +3,73 @@ import 'package:flutter/material.dart'; import 'package:paintroid/ui/theme/theme.dart'; +class BottomNavItemData { + final String label; + final Widget icon; + final VoidCallback onPressed; + + const BottomNavItemData({ + required this.label, + required this.icon, + required this.onPressed, + }); +} + class OnboardingPageBottomNavigationBar extends StatefulWidget { - final List onPressedFunctions; - final List barItems; + final List items; const OnboardingPageBottomNavigationBar( - {super.key, required this.onPressedFunctions, required this.barItems}); + {super.key, required this.items}); @override State createState() => _OnboardingPageBottomNavigationBarState(); } -class _OnboardingPageBottomNavigationBarState - extends State { - int _currentIndex = 0; - +class _OnboardingPageBottomNavigationBarState extends State { @override Widget build(BuildContext context) { - return BottomNavigationBar( - type: BottomNavigationBarType.fixed, - currentIndex: _currentIndex, - backgroundColor: PaintroidTheme.of(context).surfaceColor, - selectedItemColor: PaintroidTheme.of(context).onSurfaceColor, - unselectedItemColor: PaintroidTheme.of(context).onSurfaceColor, - selectedFontSize: 12, - unselectedFontSize: 12, - onTap: (value) { - widget.onPressedFunctions[value](); - setState(() => _currentIndex = value); - }, - items: widget.barItems, + final theme = PaintroidTheme.of(context); + + return Row( + children: List.generate(widget.items.length, (index) { + final item = widget.items[index]; + + return Expanded( + child: Container( + color: theme.surfaceColor, + child: InkWell( + onTap: () { + item.onPressed(); + setState(() =>index); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconTheme( + data: IconThemeData( + color: theme.onSurfaceColor + ), + child: item.icon, + ), + const SizedBox(height: 4), + Text( + item.label, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + color: theme.onSurfaceColor, + ), + ), + ], + ), + ), + ), + ) + ); + }), ); } -} +} \ No newline at end of file diff --git a/lib/ui/pages/onboarding_page/onboarding_page.dart b/lib/ui/pages/onboarding_page/onboarding_page.dart index 5e30ffe1..8128d966 100644 --- a/lib/ui/pages/onboarding_page/onboarding_page.dart +++ b/lib/ui/pages/onboarding_page/onboarding_page.dart @@ -62,87 +62,87 @@ class _OnboardingPageState extends State { ToastContext().init(context); return Scaffold( - body: Padding( - padding: const EdgeInsets.only(bottom: 40), - child: Column( - children: [ - Expanded( - child: PageView( - controller: _controller, - onPageChanged: (index) { - setState(() => _isLastPage = index == 4); - }, - children: const [ - Screen1(), - Screen2(), - Screen3(), - Screen4(), - Screen5(), - ], - ), - ), - Container( - height: 1, - color: PaintroidTheme.of(context).onSurfaceColor, + backgroundColor: PaintroidTheme.of(context).surfaceColor, + body: Column( + children: [ + Expanded( + child: PageView( + controller: _controller, + onPageChanged: (index) { + setState(() => _isLastPage = index == 4); + }, + children: const [ + Screen1(), + Screen2(), + Screen3(), + Screen4(), + Screen5(), + ], ), - ], - ), + ), + Container( + height: 1, + color: PaintroidTheme.of(context).onSurfaceColor, + ), + ], ), - bottomSheet: Container( - color: PaintroidTheme.of(context).surfaceColor, - height: 40, - width: MediaQuery.of(context).size.width, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(left: 30), - child: TextButton( - onPressed: () => finish(), - child: Text( - _isLastPage ? '' : 'SKIP', - style: TextStyle( - color: PaintroidTheme.of(context).onSurfaceColor, - fontSize: 15, + bottomNavigationBar: SafeArea( + child: Container( + color: PaintroidTheme.of(context).surfaceColor, + height: 40, + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 30), + child: TextButton( + onPressed: () => finish(), + child: Text( + _isLastPage ? '' : 'SKIP', + style: TextStyle( + color: PaintroidTheme.of(context).onSurfaceColor, + fontSize: 15, + ), ), ), ), - ), - SmoothPageIndicator( - count: 5, - controller: _controller, - effect: SlideEffect( - dotColor: PaintroidTheme.of(context) - .onSurfaceColor - .withValues(alpha: 0.2), - dotHeight: 8, - dotWidth: 8, - activeDotColor: PaintroidTheme.of(context).onSurfaceColor, + SmoothPageIndicator( + count: 5, + controller: _controller, + effect: SlideEffect( + dotColor: PaintroidTheme.of(context) + .onSurfaceColor + .withValues(alpha: 0.2), + dotHeight: 8, + dotWidth: 8, + activeDotColor: PaintroidTheme.of(context).onSurfaceColor, + ), ), - ), - Padding( - padding: const EdgeInsets.only(right: 30), - child: TextButton( - onPressed: () async { - if (_isLastPage) { - finish(); - } else { - _controller.nextPage( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - ); - } - }, - child: Text( - _isLastPage ? "LET'S GO" : 'NEXT', - style: TextStyle( - color: PaintroidTheme.of(context).onSurfaceColor, - fontSize: 15, + Padding( + padding: const EdgeInsets.only(right: 30), + child: TextButton( + onPressed: () async { + if (_isLastPage) { + finish(); + } else { + _controller.nextPage( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + ); + } + }, + child: Text( + _isLastPage ? "LET'S GO" : 'NEXT', + style: TextStyle( + color: PaintroidTheme.of(context).onSurfaceColor, + fontSize: 15, + ), ), ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/ui/pages/onboarding_page/screens/screen2.dart b/lib/ui/pages/onboarding_page/screens/screen2.dart index e13455a9..ba187916 100644 --- a/lib/ui/pages/onboarding_page/screens/screen2.dart +++ b/lib/ui/pages/onboarding_page/screens/screen2.dart @@ -16,6 +16,14 @@ class Screen2 extends StatefulWidget { class _Screen2State extends State { List titles = ['Tools', 'Current', 'Color', 'Layers', 'Undo', 'Redo']; + List icons = [ + 'assets/svg/ic_tools.svg', + 'assets/svg/ic_hand.svg', + '', + 'assets/svg/ic_layers.svg' + ]; + + List descriptions = [ 'Switch to the tool you want to use.', 'Shows the currently used tool and opens its options.', @@ -36,18 +44,25 @@ class _Screen2State extends State { }); } - void tools() => onPressed(0); - - void current() => onPressed(1); - - void color() => onPressed(2); - - void layers() => onPressed(3); - void undo() => onPressed(4); void redo() => onPressed(5); + List _getBottomNavItems(int l, int r) { + return List.generate( + r - l + 1, + (i) { + final index = l + i; + + return BottomNavItemData( + label: titles[index], + icon: index != 2 ? BottomBarIcon(asset: icons[index]) : Icon(Icons.check_box_outline_blank, size: 24, color: PaintroidTheme.of(context).onSurfaceColor), + onPressed: () => onPressed(index), + ); + }, + ); +} + @override Widget build(BuildContext context) { var title = Text( @@ -103,29 +118,8 @@ class _Screen2State extends State { ), ), bottomNavigationBar: OnboardingPageBottomNavigationBar( - onPressedFunctions: [tools, current, color, layers], - barItems: [ - const BottomNavigationBarItem( - label: 'Tools', - icon: BottomBarIcon(asset: 'assets/svg/ic_tools.svg'), - ), - const BottomNavigationBarItem( - label: 'Current', - icon: BottomBarIcon(asset: 'assets/svg/ic_hand.svg'), - ), - BottomNavigationBarItem( - label: 'Color', - icon: Icon( - Icons.check_box_outline_blank, - size: 24, - color: PaintroidTheme.of(context).onSurfaceColor, - ), - ), - const BottomNavigationBarItem( - label: 'Layers', - icon: BottomBarIcon(asset: 'assets/svg/ic_layers.svg')), - ], - ), + items: _getBottomNavItems(0, 3), + ) ); } } diff --git a/lib/ui/pages/onboarding_page/screens/screen3.dart b/lib/ui/pages/onboarding_page/screens/screen3.dart index bb114865..515f89f4 100644 --- a/lib/ui/pages/onboarding_page/screens/screen3.dart +++ b/lib/ui/pages/onboarding_page/screens/screen3.dart @@ -4,6 +4,7 @@ import 'package:paintroid/ui/pages/onboarding_page/components/bottom_nav_bar_con import 'package:paintroid/ui/shared/bottom_nav_bar_icon.dart'; import 'package:paintroid/ui/shared/icon_svg.dart'; import 'package:paintroid/ui/theme/theme.dart'; +import 'package:paintroid/ui/pages/onboarding_page/components/onboarding_page_bottom_nav_bar.dart'; class Screen3 extends StatefulWidget { const Screen3({super.key}); @@ -82,50 +83,20 @@ class _Screen3State extends State { }); } - void brush() => toolPressed(0); - - void hand() => toolPressed(1); - - void eraser() => toolPressed(2); - - void line() => toolPressed(3); - - void shapes() => toolPressed(4); - - void fill() => toolPressed(5); - - void sprayCan() => toolPressed(6); - - void cursor() => toolPressed(7); - - void text() => toolPressed(8); - - void stamp() => toolPressed(9); - - void transform() => toolPressed(10); - - void importImage() => toolPressed(11); - - void pipette() => toolPressed(12); - - void watercolor() => toolPressed(13); - - void smudge() => toolPressed(14); - - void clipArea() => toolPressed(15); - - List _getBottomNavigationBarItems(int l, int r) { - List items = List.generate( - r - l + 1, - (i) { - return BottomNavigationBarItem( - label: titles[l + i], - icon: BottomBarIcon(asset: icons[l + i]), - ); - }, - ); - return items; - } + List _getBottomNavItems(int l, int r) { + return List.generate( + r - l + 1, + (i) { + final index = l + i; + + return BottomNavItemData( + label: titles[index], + icon: BottomBarIcon(asset: icons[index]), + onPressed: () => toolPressed(index), + ); + }, + ); +} @override Widget build(BuildContext context) { @@ -189,28 +160,27 @@ class _Screen3State extends State { ), ], ), - ), + ), bottomNavigationBar: Column( mainAxisSize: MainAxisSize.min, children: [ BottomNavigationBarContainer( - navBarItems: _getBottomNavigationBarItems(0, 3), - onPressedFunctions: [brush, hand, eraser, line], + items: _getBottomNavItems(0, 3), + ), BottomNavigationBarContainer( - navBarItems: _getBottomNavigationBarItems(4, 7), - onPressedFunctions: [shapes, fill, sprayCan, cursor], + items: _getBottomNavItems(4, 7), + ), - BottomNavigationBarContainer( - navBarItems: _getBottomNavigationBarItems(8, 11), - onPressedFunctions: [text, stamp, transform, importImage], + BottomNavigationBarContainer( + items: _getBottomNavItems(8, 11), + ), BottomNavigationBarContainer( - navBarItems: _getBottomNavigationBarItems(12, 15), - onPressedFunctions: [pipette, watercolor, smudge, clipArea], - ), - ], - ), + items: _getBottomNavItems(12, 15), + ), + ], + ) ); } } From 5e1da6e3f0c0099cca4c14c6767e607468c2c029 Mon Sep 17 00:00:00 2001 From: Stefan Knipfer Date: Mon, 10 Aug 2026 11:31:32 +0200 Subject: [PATCH 2/2] [PAINTROID-809] Fix spray tool test --- test/integration/spray_tool_test.dart | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test/integration/spray_tool_test.dart b/test/integration/spray_tool_test.dart index a9f38b33..03810127 100644 --- a/test/integration/spray_tool_test.dart +++ b/test/integration/spray_tool_test.dart @@ -33,13 +33,14 @@ void main() { await UIInteraction.selectTool(ToolData.SPRAY.name); const radius = 50.0; + const checkRadius = 2 * radius; (UIInteraction.getCurrentTool() as SprayTool).updateSprayRadius(radius); var color = await UIInteraction.getPixelColor( CanvasPosition.centerX, CanvasPosition.centerY, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, Colors.transparent); @@ -48,7 +49,7 @@ void main() { color = await UIInteraction.getPixelColor( CanvasPosition.centerX, CanvasPosition.centerY, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, isNot(Colors.transparent)); @@ -64,13 +65,14 @@ void main() { await UIInteraction.selectTool(ToolData.SPRAY.name); const radius = 50.0; + const checkRadius = 2 * radius; (UIInteraction.getCurrentTool() as SprayTool).updateSprayRadius(radius); var color = await UIInteraction.getPixelColor( CanvasPosition.left, CanvasPosition.top, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, Colors.transparent); @@ -79,7 +81,7 @@ void main() { color = await UIInteraction.getPixelColor( CanvasPosition.left, CanvasPosition.top, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, isNot(Colors.transparent)); @@ -95,13 +97,14 @@ void main() { await UIInteraction.selectTool(ToolData.SPRAY.name); const radius = 50.0; + const checkRadius = 2 * radius; (UIInteraction.getCurrentTool() as SprayTool).updateSprayRadius(radius); var color = await UIInteraction.getPixelColor( CanvasPosition.right, CanvasPosition.bottom, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, Colors.transparent); @@ -110,7 +113,7 @@ void main() { color = await UIInteraction.getPixelColor( CanvasPosition.right, CanvasPosition.bottom, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, isNot(Colors.transparent)); @@ -126,13 +129,14 @@ void main() { await UIInteraction.selectTool(ToolData.SPRAY.name); const radius = 50.0; + const checkRadius = 2 * radius; (UIInteraction.getCurrentTool() as SprayTool).updateSprayRadius(radius); var color = await UIInteraction.getPixelColor( CanvasPosition.centerX, CanvasPosition.centerY, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, Colors.transparent); @@ -145,7 +149,7 @@ void main() { color = await UIInteraction.getPixelColor( CanvasPosition.centerX, CanvasPosition.centerY, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, isNot(Colors.transparent)); @@ -161,6 +165,7 @@ void main() { await UIInteraction.selectTool(ToolData.SPRAY.name); const radius = 50.0; + const checkRadius = 2 * radius; (UIInteraction.getCurrentTool() as SprayTool).updateSprayRadius(radius); @@ -169,7 +174,7 @@ void main() { var color = await UIInteraction.getPixelColor( CanvasPosition.centerX, CanvasPosition.centerY, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, isNot(Colors.transparent)); @@ -178,7 +183,7 @@ void main() { color = await UIInteraction.getPixelColor( CanvasPosition.centerX, CanvasPosition.centerY, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, Colors.transparent); @@ -187,7 +192,7 @@ void main() { color = await UIInteraction.getPixelColor( CanvasPosition.centerX, CanvasPosition.centerY, - radius: radius.toInt(), + radius: checkRadius.toInt(), ); expect(color, isNot(Colors.transparent)); });