Created
May 16, 2025 05:02
-
-
Save sethladd/7b14de354562016325661029150a56ec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Vibe coded in DartPad w/ Gemini on May 15 2025 | |
// I wanted to create this effect as I've always liked it. | |
// Enjoy! | |
import 'package:flutter/material.dart'; | |
import 'dart:math' as math; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Expandable Bar', | |
theme: ThemeData( | |
colorScheme: ColorScheme.fromSeed(seedColor: Colors.white), | |
useMaterial3: true, | |
), | |
home: const ExpandableBar(), | |
); | |
} | |
} | |
class ExpandableBar extends StatefulWidget { | |
const ExpandableBar({super.key}); | |
@override | |
State<ExpandableBar> createState() => _ExpandableBarState(); | |
} | |
class _ExpandableBarState extends State<ExpandableBar> { | |
double dragDistance = 0.0; | |
final double maxDragDistance = 200.0; | |
void _handleDragUpdate(DragUpdateDetails details) { | |
setState(() { | |
dragDistance -= details.delta.dy; | |
dragDistance = dragDistance.clamp(0.0, maxDragDistance); | |
}); | |
} | |
void _handleDragEnd(DragEndDetails details) { | |
setState(() { | |
if (dragDistance > maxDragDistance / 2) { | |
dragDistance = maxDragDistance; | |
} else { | |
dragDistance = 0.0; | |
} | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
double screenWidth = MediaQuery.of(context).size.width; | |
double barHeight = 50.0; | |
double verticalPositionFactor = dragDistance / maxDragDistance; | |
// Initial position of the menu icon | |
double icon1LeftStart = (screenWidth - (36 * 3)) / 2; | |
const double icon1TopStart = 7; | |
// Final position of the menu icon (top-left) | |
const double icon1LeftFinal = 16.0; | |
const double icon1TopFinal = 20.0; | |
// Curve calculation for menu icon | |
double curvedFactor = math.sin(verticalPositionFactor * math.pi / 2); | |
double icon1Left = icon1LeftStart + (icon1LeftFinal - icon1LeftStart) * curvedFactor; | |
double icon1Top = icon1TopStart + (icon1TopFinal - icon1TopStart) * curvedFactor; | |
// Initial position of the search icon | |
double icon2LeftStart = (screenWidth - (36 * 3)) / 2 + 36; | |
const double icon2TopStart = 7; | |
// Final position of the search icon (directly below menu icon) | |
const double icon2LeftFinal = 16.0; | |
const double icon2TopFinal = 20.0 + 36.0; | |
// Curve calculation for search icon | |
double icon2Left = icon2LeftStart + (icon2LeftFinal - icon2LeftStart) * curvedFactor; | |
double icon2Top = icon2TopStart + (icon2TopFinal - icon2TopStart) * curvedFactor; | |
// Initial position of the third icon (settings) | |
double icon3LeftStart = (screenWidth - (36 * 3)) / 2 + 36 + 36; // to the right of the search icon | |
const double icon3TopStart = 7; | |
// Final position of the third icon (directly below search icon) | |
const double icon3LeftFinal = 16.0; | |
const double icon3TopFinal = 20.0 + 36.0 + 36.0; | |
// Curve calculation for the third icon | |
double icon3Left = icon3LeftStart + (icon3LeftFinal - icon3LeftStart) * curvedFactor; | |
double icon3Top = icon3TopStart + (icon3TopFinal - icon3TopStart) * curvedFactor; | |
return Scaffold( | |
backgroundColor: Colors.white, | |
body: Stack( | |
alignment: Alignment.bottomCenter, | |
children: [ | |
Positioned( | |
bottom: 0, | |
child: GestureDetector( | |
onVerticalDragUpdate: _handleDragUpdate, | |
onVerticalDragEnd: _handleDragEnd, | |
child: Container( | |
width: screenWidth, | |
height: barHeight + dragDistance, | |
color: Colors.grey[200], | |
child: Stack( | |
children: [ | |
Positioned( | |
top: icon1Top, | |
left: icon1Left, | |
child: const Icon(Icons.menu, size: 36), | |
), | |
Positioned( | |
top: icon2Top, | |
left: icon2Left, | |
child: const Icon(Icons.search, size: 36), | |
), | |
Positioned( | |
top: icon3Top, | |
left: icon3Left, | |
child: const Icon(Icons.settings, size: 36), | |
), | |
], | |
), | |
), | |
), | |
), | |
], | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment