From e9b065062a8dc539a12f040f2274b78a5e26ec3e Mon Sep 17 00:00:00 2001 From: "mr.mojtaba" Date: Mon, 28 Apr 2025 12:29:13 +0330 Subject: [PATCH 1/3] feat : new bottom sheet V1 --- .../lib/presentation/root/view.dart | 139 +++++++++++++----- features/inspection/pubspec.lock | 19 ++- features/inspection/pubspec.yaml | 4 +- packages/core/pubspec.lock | 10 +- packages/core/pubspec.yaml | 2 +- pubspec.lock | 10 +- pubspec.yaml | 2 +- tools/pubspecAll.sh | 9 ++ 8 files changed, 134 insertions(+), 61 deletions(-) create mode 100644 tools/pubspecAll.sh diff --git a/features/inspection/lib/presentation/root/view.dart b/features/inspection/lib/presentation/root/view.dart index 6d70f53..e81db3f 100644 --- a/features/inspection/lib/presentation/root/view.dart +++ b/features/inspection/lib/presentation/root/view.dart @@ -11,48 +11,42 @@ class RootPage extends GetView { return Scaffold( body: Stack( children: [ - ObxValue( - (currentIndex) => IndexedStack( - index: currentIndex.value, - children: controller.pages, - ), - controller.currentIndex, - ), - - Positioned( - bottom: 0, - left: 0, - right: 0, - child: ObxValue( - (index) => BottomNavigation1( - items: [ - BottomNavigation1Item( - icon: Assets.vecMapSvg, - label: 'نقشه', - isSelected: controller.currentIndex.value == 0, - onTap: () { - controller.changePage(0); - }, + // سایر محتواها (مثلا صفحات اصلی) + Align( + alignment: Alignment.bottomCenter, + child: Column( + children: [ + const Spacer(), + Container( + height: 100, + padding: const EdgeInsets.symmetric(vertical: 10), + decoration: const BoxDecoration( + color: AppColor.blueNormal, + borderRadius: BorderRadius.vertical( + top: Radius.circular(40), + ), ), - BottomNavigation1Item( - icon: Assets.vecSettingSvg, - label: 'اقدام', - isSelected: controller.currentIndex.value == 1, - onTap: () { - controller.changePage(1); - }, + child: SingleChildScrollView( + clipBehavior: Clip.none, + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Row( + children: List.generate( + 50, + (index) => ObxValue((data) { + return BottomNavigation1ItemTST( + icon: Assets.vecMapSvg, + label: 'item$index', + isSelected: controller.currentIndex.value == index, + onTap: () => controller.changePage(index), + ); + }, controller.currentIndex), + ), + ), ), - BottomNavigation1Item( - icon: Assets.vecProfileCircleSvg, - label: 'پروفایل', - isSelected: controller.currentIndex.value == 2, - onTap: () { - controller.changePage(2); - }, - ), - ], - ), - controller.currentIndex, + ), + ], ), ), ], @@ -60,3 +54,68 @@ class RootPage extends GetView { ); } } + +class BottomNavigation1ItemTST extends StatelessWidget { + final String icon; + final String label; + final bool isSelected; + final Function() onTap; + + const BottomNavigation1ItemTST({ + super.key, + required this.icon, + required this.label, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 80, + height: 130, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.bottomCenter, + children: [ + AnimatedPositioned( + duration: const Duration(milliseconds: 400), + width: 80, + height: 80, + bottom: isSelected ? 50 : 0, + child: InkWell( + splashColor: Colors.transparent, + onTap: onTap, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: isSelected ? AppColor.greenNormal : Colors.transparent, + borderRadius: BorderRadius.circular(40), + border: + isSelected + ? Border.all(width: 2, color: Colors.white) + : null, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + vecWidget(icon, width: 32, height: 32, color: Colors.white), + const SizedBox(height: 7), + Text( + label, + style: AppFonts.yekan14.copyWith(color: Colors.white), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } +} + + + diff --git a/features/inspection/pubspec.lock b/features/inspection/pubspec.lock index 415d395..eeeefab 100644 --- a/features/inspection/pubspec.lock +++ b/features/inspection/pubspec.lock @@ -94,6 +94,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_map: dependency: "direct main" description: @@ -159,18 +164,18 @@ packages: dependency: "direct main" description: name: geolocator - sha256: f62bcd90459e63210bbf9c35deb6a51c521f992a78de19a1fe5c11704f9530e2 + sha256: e7ebfa04ce451daf39b5499108c973189a71a919aa53c1204effda1c5b93b822 url: "https://pub.dev" source: hosted - version: "13.0.4" + version: "14.0.0" geolocator_android: dependency: transitive description: name: geolocator_android - sha256: fcb1760a50d7500deca37c9a666785c047139b5f9ee15aa5469fae7dbbe3170d + sha256: "114072db5d1dce0ec0b36af2697f55c133bc89a2c8dd513e137c0afe59696ed4" url: "https://pub.dev" source: hosted - version: "4.6.2" + version: "5.0.1+1" geolocator_apple: dependency: transitive description: @@ -255,10 +260,10 @@ packages: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" json_annotation: dependency: transitive description: @@ -656,5 +661,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.7.2 <4.0.0" flutter: ">=3.27.0" diff --git a/features/inspection/pubspec.yaml b/features/inspection/pubspec.yaml index cea82c7..b577dea 100644 --- a/features/inspection/pubspec.yaml +++ b/features/inspection/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' version: 1.0.0 environment: - sdk: ^3.7.0 + sdk: ^3.7.2 dependencies: flutter: @@ -15,4 +15,4 @@ dependencies: flutter_map_animations: ^0.9.0 location: ^8.0.0 latlong2: ^0.9.1 - geolocator: ^13.0.4 + geolocator: ^14.0.0 diff --git a/packages/core/pubspec.lock b/packages/core/pubspec.lock index f5a3aee..0bfb36b 100644 --- a/packages/core/pubspec.lock +++ b/packages/core/pubspec.lock @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.12.0" boolean_selector: dependency: transitive description: @@ -189,10 +189,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.2" ffi: dependency: transitive description: @@ -939,5 +939,5 @@ packages: source: hosted version: "2.1.0" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.7.2 <4.0.0" flutter: ">=3.27.0" diff --git a/packages/core/pubspec.yaml b/packages/core/pubspec.yaml index 4df8b74..edb8b47 100644 --- a/packages/core/pubspec.yaml +++ b/packages/core/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: ^3.7.0 + sdk: ^3.7.2 dependencies: flutter: diff --git a/pubspec.lock b/pubspec.lock index 8f91457..1f5a0d1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.12.0" boolean_selector: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.2" ffi: dependency: transitive description: @@ -769,5 +769,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.7.2 <4.0.0" flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 6916a57..200f78e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: ^3.7.0 + sdk: ^3.7.2 dependencies: flutter: diff --git a/tools/pubspecAll.sh b/tools/pubspecAll.sh new file mode 100644 index 0000000..9c22f36 --- /dev/null +++ b/tools/pubspecAll.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Navigate to the project root (assuming tools is one level below root) +cd "$(dirname "$0")/.." + +# Find all pubspec.yaml files, print their directories, and run flutter pub get +#find . -name "pubspec.yaml" -exec sh -c 'echo "Running flutter pub get in $(dirname {}) 🙂🙂🙂🙂🙂"; cd $(dirname {}) ; flutter pub get' \; + +find . -name "pubspec.yaml" -exec sh -c 'echo "Running flutter pub get in $(dirname {}) ⬆️🆙🆙🆙⬆️"; cd $(dirname {}) ; flutter pub upgrade' \; \ No newline at end of file From caa388424221558f29c18f51a532d9f34390b85a Mon Sep 17 00:00:00 2001 From: "mr.mojtaba" Date: Mon, 28 Apr 2025 17:06:21 +0330 Subject: [PATCH 2/3] feat : new bottom sheet V2 --- .../lib/presentation/root/view.dart | 331 ++++++++++++++++-- 1 file changed, 301 insertions(+), 30 deletions(-) diff --git a/features/inspection/lib/presentation/root/view.dart b/features/inspection/lib/presentation/root/view.dart index e81db3f..b886a3f 100644 --- a/features/inspection/lib/presentation/root/view.dart +++ b/features/inspection/lib/presentation/root/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_core/core.dart'; +import 'package:rasadyar_core/data/utils.dart'; import 'logic.dart'; @@ -14,39 +15,34 @@ class RootPage extends GetView { // سایر محتواها (مثلا صفحات اصلی) Align( alignment: Alignment.bottomCenter, - child: Column( - children: [ - const Spacer(), - Container( - height: 100, - padding: const EdgeInsets.symmetric(vertical: 10), - decoration: const BoxDecoration( - color: AppColor.blueNormal, - borderRadius: BorderRadius.vertical( - top: Radius.circular(40), - ), - ), - child: SingleChildScrollView( - clipBehavior: Clip.none, - scrollDirection: Axis.horizontal, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - children: List.generate( - 50, - (index) => ObxValue((data) { - return BottomNavigation1ItemTST( - icon: Assets.vecMapSvg, - label: 'item$index', - isSelected: controller.currentIndex.value == index, - onTap: () => controller.changePage(index), - ); - }, controller.currentIndex), + child: Container( + height: 90, + color: AppColor.blueNormal, + clipBehavior: Clip.none, + padding: const EdgeInsets.all(10), + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Positioned( + top: -43, + child: Container( + width: 110, + height: 110, + clipBehavior: Clip.none, + decoration: ShapeDecoration( + color:Colors.white, + + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(200), + side: BorderSide(width: 2, color: Colors.white), + ), ), ), ), - ), - ], + WavePageView(), + ], + ), ), ), ], @@ -117,5 +113,280 @@ class BottomNavigation1ItemTST extends StatelessWidget { } } +class ScrollWithCenterScaling extends StatefulWidget { + const ScrollWithCenterScaling({super.key}); + @override + State createState() => + _ScrollWithCenterScalingState(); +} +class _ScrollWithCenterScalingState extends State { + final PageController _pageController = PageController(viewportFraction: 0.3); + double currentPage = 0.0; + + @override + void initState() { + super.initState(); + _pageController.addListener(() { + setState(() { + currentPage = _pageController.page ?? 0.0; + }); + }); + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + double _calculateScale(int index) { + final double distance = (currentPage - index).abs(); + if (distance >= 1) { + return 0.7; + } else { + return 1.3; + } + } + + bool _isSelected(int index) { + return (currentPage - index).abs() < 0.5; // دقیق وسط بودن رو بگیره + } + + @override + Widget build(BuildContext context) { + return PageView.builder( + controller: _pageController, + itemCount: 50, + clipBehavior: Clip.none, + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + final scale = _calculateScale(index); + final selected = _isSelected(index); + + return Transform.scale( + scale: scale, + child: AnimatedPadding( + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + padding: EdgeInsets.only(bottom: selected ? 20 : 0), + child: InkWell( + onTap: () {}, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.map, color: Colors.white), + const SizedBox(height: 8), + Text( + 'Label $index', + style: TextStyle( + color: Colors.white, + fontSize: selected ? 18 : 0, + fontWeight: + selected ? FontWeight.bold : FontWeight.normal, + ), + ), + ], + ), + ), + ), + ); + }, + ); + } +} + +class ScrollWithCenterRotation2 extends StatefulWidget { + const ScrollWithCenterRotation2({super.key}); + + @override + State createState() => + _ScrollWithCenterRotationState2(); +} + +class _ScrollWithCenterRotationState2 extends State { + final PageController _pageController = PageController(viewportFraction: 0.3); + double currentPage = 0.0; + + @override + void initState() { + super.initState(); + _pageController.addListener(() { + setState(() { + currentPage = _pageController.page ?? 0.0; + }); + }); + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + double _calculateScale(int index) { + final double distance = (currentPage - index).abs(); + if (distance >= 1) { + return 0.7; + } else { + return 1.2; + } + } + + double _calculateRotationY(int index) { + return (currentPage - index) * 0.5; + } + + bool _isSelected(int index) { + return (currentPage - index).abs() < 0.5; + } + + @override + Widget build(BuildContext context) { + return PageView.builder( + controller: _pageController, + itemCount: 50, + clipBehavior: Clip.none, + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + final scale = _calculateScale(index); + final rotationY = _calculateRotationY(index); + final selected = _isSelected(index); + + return AnimatedContainer( + clipBehavior: Clip.none, + height: 80, + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + margin: EdgeInsets.only( + bottom: selected ? 90 : 20, // وسطی بالاتر میاد + ), + child: Transform( + transform: + Matrix4.identity() + ..scale(scale) + ..setEntry(3, 2, 0.001), + alignment: Alignment.center, + child: InkWell( + onTap: () {}, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: selected ? 70 : 60, + height: selected ? 70 : 60, + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + boxShadow: + selected + ? [ + BoxShadow( + color: Colors.amber, + blurRadius: 20, + spreadRadius: 5, + ), + ] + : [], + ), + alignment: Alignment.center, + child: Icon( + Icons.map, + color: Colors.white, + size: selected ? 32 : 24, + ), + ), + const SizedBox(height: 8), + Text( + 'Label $index', + style: TextStyle( + color: Colors.white, + fontSize: selected ? 18 : 14, + fontWeight: + selected ? FontWeight.bold : FontWeight.normal, + ), + ), + ], + ), + ), + ), + ); + }, + ); + } +} + +class WavePageView extends StatefulWidget { + @override + _WavePageViewState createState() => _WavePageViewState(); +} + +class _WavePageViewState extends State { + final PageController _controller = PageController(viewportFraction: 0.3); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + double _calculateScale(double currentPage,int index) { + final double distance = (currentPage - index).abs(); + if (distance >= 1) { + return 0.7; + } else { + return 1.7; + } + } + + @override + Widget build(BuildContext context) { + return PageView.builder( + controller: _controller, + clipBehavior: Clip.none, + scrollDirection: Axis.horizontal, + itemCount: 10, + + itemBuilder: (context, index) { + return AnimatedBuilder( + animation: _controller, + builder: (context, child) { + double value = 0.0; + final scale = _calculateScale(_controller.page??0,index); + value = index - (_controller.page ?? 0); + value = (value).clamp(-1, 1); + + eLog("index = $index \n value = $value"); + + double offset = value * 30; + if (value.abs() < 0.1 || value.abs() > 0.1) { + offset = -5 * (1 - value.abs() * 5); + } + + return Transform.scale( + scale: scale, + child: Transform.translate( + offset: Offset(0, offset), + child: Column( + children: [ + Icon(Icons.map, color: value.toInt() == 0 ? AppColor.greenNormal:Colors.white, size: 32), + + Visibility( + visible: value.toInt() == 0, + child: Text( + 'بازرسی', + style: AppFonts.yekan10.copyWith(color: AppColor.greenNormal) + ), + ), + ], + ), + ), + ); + }, + ); + }, + ); + } +} From bb4eccd01dc908dd7ac158646150217c03e142c0 Mon Sep 17 00:00:00 2001 From: "mr.mojtaba" Date: Tue, 29 Apr 2025 16:11:13 +0330 Subject: [PATCH 3/3] feat : wave bottom sheet :) --- .../lib/presentation/root/view.dart | 401 ++---------------- .../wave_bottom_navigation.dart | 132 ++++++ .../core/lib/presentation/widget/widget.dart | 22 +- 3 files changed, 174 insertions(+), 381 deletions(-) create mode 100644 packages/core/lib/presentation/widget/bottom_navigation/wave_bottom_navigation.dart diff --git a/features/inspection/lib/presentation/root/view.dart b/features/inspection/lib/presentation/root/view.dart index b886a3f..4ce262b 100644 --- a/features/inspection/lib/presentation/root/view.dart +++ b/features/inspection/lib/presentation/root/view.dart @@ -12,37 +12,40 @@ class RootPage extends GetView { return Scaffold( body: Stack( children: [ - // سایر محتواها (مثلا صفحات اصلی) + Align( alignment: Alignment.bottomCenter, - child: Container( - height: 90, - color: AppColor.blueNormal, - clipBehavior: Clip.none, - padding: const EdgeInsets.all(10), - child: Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Positioned( - top: -43, - child: Container( - width: 110, - height: 110, - clipBehavior: Clip.none, - decoration: ShapeDecoration( - color:Colors.white, - - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(200), - side: BorderSide(width: 2, color: Colors.white), - ), - ), - ), - ), - WavePageView(), - ], - ), + child: WaveBottomNavigation( + items: [ + WaveBottomNavigationItem( + title: 'خانه', + icon:Assets.vecMapSvg, + ), + WaveBottomNavigationItem( + title: 'عملیات', + icon:Assets.vecUserSvg, + ), + WaveBottomNavigationItem( + title: 'افزودن', + icon:Assets.vecAddSvg, + ), + WaveBottomNavigationItem( + title: 'آمار', + icon:Assets.vecDiagramSvg, + ), WaveBottomNavigationItem( + title: 'تماس', + icon:Assets.vecCallSvg, + ), WaveBottomNavigationItem( + title: 'مکان ', + icon:Assets.vecGpsSvg, + ), WaveBottomNavigationItem( + title: 'تاریخ', + icon:Assets.vecCalendarSvg, + ), + ], + onPageChanged: (index) { + controller.changePage(index); + }, ), ), ], @@ -50,343 +53,3 @@ class RootPage extends GetView { ); } } - -class BottomNavigation1ItemTST extends StatelessWidget { - final String icon; - final String label; - final bool isSelected; - final Function() onTap; - - const BottomNavigation1ItemTST({ - super.key, - required this.icon, - required this.label, - required this.isSelected, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 80, - height: 130, - child: Stack( - clipBehavior: Clip.none, - alignment: Alignment.bottomCenter, - children: [ - AnimatedPositioned( - duration: const Duration(milliseconds: 400), - width: 80, - height: 80, - bottom: isSelected ? 50 : 0, - child: InkWell( - splashColor: Colors.transparent, - onTap: onTap, - child: Container( - width: 80, - height: 80, - decoration: BoxDecoration( - color: isSelected ? AppColor.greenNormal : Colors.transparent, - borderRadius: BorderRadius.circular(40), - border: - isSelected - ? Border.all(width: 2, color: Colors.white) - : null, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - vecWidget(icon, width: 32, height: 32, color: Colors.white), - const SizedBox(height: 7), - Text( - label, - style: AppFonts.yekan14.copyWith(color: Colors.white), - ), - ], - ), - ), - ), - ), - ], - ), - ); - } -} - -class ScrollWithCenterScaling extends StatefulWidget { - const ScrollWithCenterScaling({super.key}); - - @override - State createState() => - _ScrollWithCenterScalingState(); -} - -class _ScrollWithCenterScalingState extends State { - final PageController _pageController = PageController(viewportFraction: 0.3); - double currentPage = 0.0; - - @override - void initState() { - super.initState(); - _pageController.addListener(() { - setState(() { - currentPage = _pageController.page ?? 0.0; - }); - }); - } - - @override - void dispose() { - _pageController.dispose(); - super.dispose(); - } - - double _calculateScale(int index) { - final double distance = (currentPage - index).abs(); - if (distance >= 1) { - return 0.7; - } else { - return 1.3; - } - } - - bool _isSelected(int index) { - return (currentPage - index).abs() < 0.5; // دقیق وسط بودن رو بگیره - } - - @override - Widget build(BuildContext context) { - return PageView.builder( - controller: _pageController, - itemCount: 50, - clipBehavior: Clip.none, - physics: const BouncingScrollPhysics(), - scrollDirection: Axis.horizontal, - itemBuilder: (context, index) { - final scale = _calculateScale(index); - final selected = _isSelected(index); - - return Transform.scale( - scale: scale, - child: AnimatedPadding( - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, - padding: EdgeInsets.only(bottom: selected ? 20 : 0), - child: InkWell( - onTap: () {}, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.map, color: Colors.white), - const SizedBox(height: 8), - Text( - 'Label $index', - style: TextStyle( - color: Colors.white, - fontSize: selected ? 18 : 0, - fontWeight: - selected ? FontWeight.bold : FontWeight.normal, - ), - ), - ], - ), - ), - ), - ); - }, - ); - } -} - -class ScrollWithCenterRotation2 extends StatefulWidget { - const ScrollWithCenterRotation2({super.key}); - - @override - State createState() => - _ScrollWithCenterRotationState2(); -} - -class _ScrollWithCenterRotationState2 extends State { - final PageController _pageController = PageController(viewportFraction: 0.3); - double currentPage = 0.0; - - @override - void initState() { - super.initState(); - _pageController.addListener(() { - setState(() { - currentPage = _pageController.page ?? 0.0; - }); - }); - } - - @override - void dispose() { - _pageController.dispose(); - super.dispose(); - } - - double _calculateScale(int index) { - final double distance = (currentPage - index).abs(); - if (distance >= 1) { - return 0.7; - } else { - return 1.2; - } - } - - double _calculateRotationY(int index) { - return (currentPage - index) * 0.5; - } - - bool _isSelected(int index) { - return (currentPage - index).abs() < 0.5; - } - - @override - Widget build(BuildContext context) { - return PageView.builder( - controller: _pageController, - itemCount: 50, - clipBehavior: Clip.none, - physics: const BouncingScrollPhysics(), - scrollDirection: Axis.horizontal, - itemBuilder: (context, index) { - final scale = _calculateScale(index); - final rotationY = _calculateRotationY(index); - final selected = _isSelected(index); - - return AnimatedContainer( - clipBehavior: Clip.none, - height: 80, - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, - margin: EdgeInsets.only( - bottom: selected ? 90 : 20, // وسطی بالاتر میاد - ), - child: Transform( - transform: - Matrix4.identity() - ..scale(scale) - ..setEntry(3, 2, 0.001), - alignment: Alignment.center, - child: InkWell( - onTap: () {}, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: selected ? 70 : 60, - height: selected ? 70 : 60, - decoration: BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - boxShadow: - selected - ? [ - BoxShadow( - color: Colors.amber, - blurRadius: 20, - spreadRadius: 5, - ), - ] - : [], - ), - alignment: Alignment.center, - child: Icon( - Icons.map, - color: Colors.white, - size: selected ? 32 : 24, - ), - ), - const SizedBox(height: 8), - Text( - 'Label $index', - style: TextStyle( - color: Colors.white, - fontSize: selected ? 18 : 14, - fontWeight: - selected ? FontWeight.bold : FontWeight.normal, - ), - ), - ], - ), - ), - ), - ); - }, - ); - } -} - -class WavePageView extends StatefulWidget { - @override - _WavePageViewState createState() => _WavePageViewState(); -} - -class _WavePageViewState extends State { - final PageController _controller = PageController(viewportFraction: 0.3); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - double _calculateScale(double currentPage,int index) { - final double distance = (currentPage - index).abs(); - if (distance >= 1) { - return 0.7; - } else { - return 1.7; - } - } - - @override - Widget build(BuildContext context) { - return PageView.builder( - controller: _controller, - clipBehavior: Clip.none, - scrollDirection: Axis.horizontal, - itemCount: 10, - - itemBuilder: (context, index) { - return AnimatedBuilder( - animation: _controller, - builder: (context, child) { - double value = 0.0; - final scale = _calculateScale(_controller.page??0,index); - value = index - (_controller.page ?? 0); - value = (value).clamp(-1, 1); - - eLog("index = $index \n value = $value"); - - double offset = value * 30; - if (value.abs() < 0.1 || value.abs() > 0.1) { - offset = -5 * (1 - value.abs() * 5); - } - - return Transform.scale( - scale: scale, - child: Transform.translate( - offset: Offset(0, offset), - child: Column( - children: [ - Icon(Icons.map, color: value.toInt() == 0 ? AppColor.greenNormal:Colors.white, size: 32), - - Visibility( - visible: value.toInt() == 0, - child: Text( - 'بازرسی', - style: AppFonts.yekan10.copyWith(color: AppColor.greenNormal) - ), - ), - ], - ), - ), - ); - }, - ); - }, - ); - } -} diff --git a/packages/core/lib/presentation/widget/bottom_navigation/wave_bottom_navigation.dart b/packages/core/lib/presentation/widget/bottom_navigation/wave_bottom_navigation.dart new file mode 100644 index 0000000..95b808f --- /dev/null +++ b/packages/core/lib/presentation/widget/bottom_navigation/wave_bottom_navigation.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; + +class WaveBottomNavigationItem { + final String title; + final String icon; + + WaveBottomNavigationItem({required this.title, required this.icon}); +} + +class WaveBottomNavigation extends StatefulWidget { + const WaveBottomNavigation({ + super.key, + required this.items, + required this.onPageChanged, + }); + + final List items; + final Function(int) onPageChanged; + + @override + State createState() => _WaveBottomNavigationState(); +} + +class _WaveBottomNavigationState extends State { + final PageController _controller = PageController(viewportFraction: 0.3); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + double _calculateScale(double currentPage, int index) { + final double distance = (currentPage - index).abs(); + if (distance >= 1) { + return 0.9; + } else { + return 1.50; + } + } + + @override + Widget build(BuildContext context) { + return Container( + height: 68, + color: AppColor.blueNormal, + clipBehavior: Clip.none, + padding: const EdgeInsets.all(10), + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [backgroundWidget(), waveScrolledLists()], + ), + ); + } + + Widget backgroundWidget() { + return Positioned( + top: -43, + child: Container( + width: 90, + height: 90, + clipBehavior: Clip.none, + decoration: ShapeDecoration( + color: AppColor.greenNormal, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(200), + side: BorderSide(width: 5, color: Colors.white), + ), + ), + ), + ); + } + + Widget waveScrolledLists() { + return PageView.builder( + controller: _controller, + clipBehavior: Clip.none, + onPageChanged: widget.onPageChanged, + scrollDirection: Axis.horizontal, + itemCount: widget.items.length, + physics: const ClampingScrollPhysics(), + itemBuilder: (context, index) { + final WaveBottomNavigationItem item = widget.items[index]; + return Center( + child: AnimatedBuilder( + animation: _controller, + builder: (context, child) { + double value = 0.0; + final scale = _calculateScale(_controller.page ?? 0, index); + value = index - (_controller.page ?? 0); + value = (value).clamp(-1, 1); + double offset = value * 30; + if (value.abs() < 0.2 || value.abs() > 0.2) { + offset = -7 * (1 - value.abs() * 2); + } + + return Transform.scale( + scale: scale, + child: Transform.translate( + offset: Offset(0, offset), + child: Column( + children: [ + Tooltip( + message: item.title, + child: vecWidget( + item.icon, + color: Colors.white, + width: 32, + height: 32, + ), + ), + + /* Visibility( + visible: (_controller.page ?? 0) == index, + child: Text( + item.title, + style: AppFonts.yekan10.copyWith(color: Colors.white), + ), + ),*/ + ], + ), + ), + ); + }, + ), + ); + }, + ); + } +} diff --git a/packages/core/lib/presentation/widget/widget.dart b/packages/core/lib/presentation/widget/widget.dart index 0df4a98..4f6869c 100644 --- a/packages/core/lib/presentation/widget/widget.dart +++ b/packages/core/lib/presentation/widget/widget.dart @@ -1,18 +1,16 @@ -export 'vec_widget.dart'; +export 'app_bar/r_app_bar.dart'; export 'bottom_navigation/bottom_navigation_1.dart'; +export 'bottom_navigation/wave_bottom_navigation.dart'; +export 'buttons/elevated.dart'; +export 'buttons/outline_elevated.dart'; +export 'buttons/outline_elevated_icon.dart'; +export 'buttons/text_button.dart'; +export 'captcha/captcha_widget.dart'; export 'draggable_bottom_sheet/draggable_bottom_sheet.dart'; export 'draggable_bottom_sheet/draggable_bottom_sheet_controller.dart'; -export 'buttons/outline_elevated_icon.dart'; -export 'buttons/outline_elevated.dart'; -export 'buttons/text_button.dart'; -export 'app_bar/r_app_bar.dart'; -export 'captcha/captcha_widget.dart'; -export 'buttons/elevated.dart'; export 'inputs/r_input.dart'; -export 'tabs/new_tab.dart'; -export 'tabs/tab.dart'; export 'pagination/pagination_from_until.dart'; export 'pagination/show_more.dart'; - - - +export 'tabs/new_tab.dart'; +export 'tabs/tab.dart'; +export 'vec_widget.dart';