Created
December 23, 2023 17:36
-
-
Save PlugFox/ed60c155e7792d92b5400ca1652f0c2c to your computer and use it in GitHub Desktop.
Side panel example
This file contains 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
/* | |
* Side panel example | |
* https://gist.github.com/PlugFox/ed60c155e7792d92b5400ca1652f0c2c | |
* https://dartpad.dev?id=ed60c155e7792d92b5400ca1652f0c2c | |
* Matiunin Mikhail <plugfox@gmail.com>, 23 December 2023 | |
*/ | |
import 'dart:async'; | |
import 'dart:developer'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
void main() => runZonedGuarded<void>( | |
() => runApp(const App()), | |
(error, stackTrace) => log('Top level exception $error'), | |
); | |
class App extends StatelessWidget { | |
const App({super.key}); | |
@override | |
Widget build(BuildContext context) => MaterialApp( | |
title: 'Side menu example', | |
theme: ThemeData.light(), | |
home: const HomeScreen(), | |
builder: (context, child) => SideMenuScope( | |
menu: const SideMenu(), | |
child: child ?? const SizedBox.shrink(), | |
), | |
); | |
} | |
class SideMenuScope extends StatelessWidget { | |
const SideMenuScope({ | |
required this.menu, | |
required this.child, | |
this.width = 256, | |
super.key, // ignore: unused_element | |
}); | |
final Widget menu; | |
final Widget child; | |
final double width; | |
Widget _materialContext({required Widget child}) => Material( | |
child: ScaffoldMessenger( | |
child: DefaultSelectionStyle( | |
child: AnimatedTheme( | |
data: ThemeData.dark(), | |
duration: const Duration(milliseconds: 350), | |
curve: Curves.easeInOut, | |
child: HeroControllerScope.none( | |
child: Navigator( | |
pages: <Page<void>>[ | |
MaterialPage<void>( | |
child: Scaffold( | |
body: child, | |
), | |
), | |
], | |
onPopPage: (route, result) => route.didPop(result), | |
), | |
), | |
), | |
), | |
), | |
); | |
@override | |
Widget build(BuildContext context) { | |
final mediaQuery = MediaQuery.of(context); | |
final width = mediaQuery.size.width / 3 < this.width ? .0 : this.width; | |
return Stack( | |
children: <Widget>[ | |
if (width > 0) | |
Positioned( | |
key: const ValueKey('menu'), | |
left: 0, | |
width: width, | |
top: 0, | |
bottom: 0, | |
child: MediaQuery( | |
data: mediaQuery.copyWith( | |
size: Size(width, mediaQuery.size.height), | |
), | |
child: _materialContext( | |
child: menu, | |
), | |
), | |
), | |
Positioned( | |
key: const ValueKey('content'), | |
left: width > 0 ? width : 0, | |
right: 0, | |
top: 0, | |
bottom: 0, | |
child: MediaQuery( | |
data: mediaQuery.copyWith( | |
size: Size( | |
mediaQuery.size.width - width, | |
mediaQuery.size.height, | |
), | |
), | |
child: child), | |
), | |
], | |
); | |
} | |
} | |
class SideMenu extends StatefulWidget { | |
const SideMenu({ | |
super.key, // ignore: unused_element | |
}); | |
@override | |
State<SideMenu> createState() => _SideMenuState(); | |
} | |
class _SideMenuState extends State<SideMenu> { | |
final TextEditingController _usernameController = TextEditingController(), | |
_passwordController = TextEditingController(); | |
@override | |
Widget build(BuildContext context) => Center( | |
child: Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 16), | |
child: AutofillGroup( | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
SizedBox( | |
height: 64, | |
child: TextField( | |
controller: _usernameController, | |
autofillHints: const [AutofillHints.username], | |
decoration: const InputDecoration( | |
labelText: 'Email', | |
hintText: 'Enter your email', | |
border: OutlineInputBorder(), | |
), | |
), | |
), | |
const SizedBox(height: 8), | |
SizedBox( | |
height: 64, | |
child: TextField( | |
controller: _passwordController, | |
autofillHints: const [AutofillHints.password], | |
obscureText: true, | |
decoration: const InputDecoration( | |
labelText: 'Password', | |
hintText: 'Enter your password', | |
border: OutlineInputBorder(), | |
), | |
), | |
), | |
const Divider( | |
height: 32, | |
thickness: 1, | |
), | |
SizedBox( | |
height: 64, | |
child: ElevatedButton( | |
onPressed: () { | |
_usernameController.clear(); | |
_passwordController.clear(); | |
ScaffoldMessenger.of(context).showSnackBar( | |
const SnackBar( | |
content: Text('Login'), | |
), | |
); | |
HapticFeedback.lightImpact(); | |
}, | |
child: const Text('Login'), | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
class HomeScreen extends StatelessWidget { | |
const HomeScreen({ | |
super.key, // ignore: unused_element | |
}); | |
@override | |
Widget build(BuildContext context) => Scaffold( | |
appBar: AppBar( | |
title: const Text('Home'), | |
), | |
body: const SafeArea( | |
child: Center( | |
child: Placeholder(), | |
), | |
), | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment