Flutter: How to implement floating SearchBar (Custom AppBar) with Drawer like Google Apps - flutter

Can somebody please tell me how can I integrate the menu drawer inside the Row widget instead of in a Scaffold widget? Something like Gmail's app (search with drawer icon).

It's very simple.
Screenshot of the output
Steps:
Step 1:
Define the scaffold with a custom Appbar widget
return Scaffold(
appBar: FloatAppBar(),
body: Center(
child: Text('Body'),
),
drawer: Drawer(
child: SafeArea(
right: false,
child: Center(
child: Text('Drawer content'),
),
),
),
);
Step 2:
Implement the PreferredSizeWidget to create a custom AppBar
class FloatAppBar extends StatelessWidget with PreferredSizeWidget {
step 3:
Use Scaffold.of(context).openDrawer(); to open the drawer when required.
Here is the complete snippet.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Playground',
home: TestPage(),
);
}
}
class TestPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: FloatAppBar(),
body: Center(
child: Text('Body'),
),
drawer: Drawer(
child: SafeArea(
right: false,
child: Center(
child: Text('Drawer content'),
),
),
),
);
}
}
class FloatAppBar extends StatelessWidget with PreferredSizeWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
top: 10,
right: 15,
left: 15,
child: Container(
color: Colors.white,
child: Row(
children: <Widget>[
Material(
type: MaterialType.transparency,
child: IconButton(
splashColor: Colors.grey,
icon: Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
),
),
Expanded(
child: TextField(
cursorColor: Colors.black,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.go,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 15),
hintText: "Search..."),
),
),
],
),
),
),
],
);
}
#override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}
See the live demo here.

the AppBar widget alredy has mechanisms for that,
AppBar(
drawaer: YourDrawer(),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: (){}
)
]
);
it will create the Gmail appbar you want

Related

the solution of Navigator operation requested with a context that does not include a Navigator

I am trying to add an icon button that will navigate to another route onPressed, and there's no error until I run the app and it shows me Navigator operation requested with a context that does not include a Navigator. What am I missing? Thanks. I deleted codes inside scaffold to be able to post this question
class SignIn extends StatelessWidget {
const SignIn({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(fontFamily: 'Raleway'),
home: Scaffold (
),
body:
Container(
child:
Column(children: [
Text('Sign In', style: TextStyle(
color: Colors.orange,
fontSize: 60.0) ),
SizedBox(height:20,),
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Email',
),
),
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Password',
),
),
SizedBox(height: 20,),
ElevatedButton(
child: const Text('Sign In'),
onPressed: null,
),
SizedBox(height: 15,),
Text('Forgot Password ?', style: TextStyle(fontSize: 15),)
],
),
margin: EdgeInsets.only(top: 200),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Colors.purple,
),
child: Image(image: AssetImage('Images/smsa logo.png'))
),
ListTile(
title: Text('My Account'),
leading: IconButton(onPressed: (){Navigator.push(
context,
MaterialPageRoute(builder: (context) => new MyAccount() ));
}, icon: Icon(Icons.account_circle)
)),
The problem is that even though you have a MaterialApp inside your widgets, you don't have access to its context, but rather the context which is being passed above the material app. To solve this problem you have to extract the MaterialApp in its own widget
This example which you can paste on DartPad shows you how to do it:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) { // the context you access here has an MaterialApp above it
return Text(
'Hello, World!',
style: Theme.of(context).textTheme.headline4,
);
}
}

How do I implement a Drawer menu in my pages?

I have a dart file as a 'MyDrawer' and I want to add this drawer menu to my dashboard page. Here is my code. I don't know why it doesn't work.
import 'package:flutter/material.dart';
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Menu drawer'),
decoration: BoxDecoration(
color: Colors.red,
),
),
ListTile(
leading: Icon(Icons.home, size: 40,),
title: Text('First item'),
subtitle: Text("This is the 1st item"),
trailing: Icon(Icons.more_vert),
onTap: () {
},
),
ListTile(
title: Text('Second item'),
onTap: () {
},
),
],
),
);}}
And I'm calling this drawer menu in my dashboard page. It's like that;
class Dash extends StatefulWidget { ...
class _DashState extends State<Dash> { ...
#override
void initState(){ ...
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: MyDrawer(),
body: Container(
... ),
Your drawer MyDrawer should be a StatelessWidget that returns a Drawer.
This is how you should do it:
class DashboardPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Menu drawer'),
decoration: BoxDecoration(
color: Colors.red,
),
),
ListTile(
leading: Icon(Icons.home, size: 40,),
title: Text('First item'),
subtitle: Text("This is the 1st item"),
trailing: Icon(Icons.more_vert),
onTap: () {
},
),
ListTile(
title: Text('Second item'),
onTap: () {
},
),
],
),
);
}
}
All I needed was to add an appbar. I definitely missed these details. Here is the updated version.
class Dash extends StatefulWidget { ...
class _DashState extends State<Dash> { ...
#override
void initState(){ ...
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
body: Container(
... ),

How to have a drawer with 2 separate colors in Flutter

I'm trying to figure out how to have the green fill up the entire drawer space under the yellow header. Right now I have my ListTiles wrapped in a Column, in a Container, with the Container color set to green. All help is appreciated.
What I have so far
One of the ways could be wrapping the ListView that you are probably using with a Container that has color green. Please see the code below :
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Flutter Demo"),
),
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.white,
),
drawer: Container(
color: Colors.green,
child: ListView(
children: <Widget>[
DrawerHeader(
padding: const EdgeInsets.all(0),
child: Container(
child: const Center(child: Text("Profile")),
color: Colors.yellow,
),
),
ListTile(
title: const Text('Item 1'),
tileColor: Colors.green,
onTap: () {
Navigator.pop(context);
},
),
ListTile(
title: const Text('Item 2'),
tileColor: Colors.green,
onTap: () {
Navigator.pop(context);
},
),
],
),
),
);
}
}

Reorderable List glitches keyboard when not on home screen

I am working on a project with textfields that are members of a reorderable list. My problem is when the list is accessed from a Navigator push to a new screen the text fields within the ReorderableListView on the new screen glitches causing the keyboard to pop up then quickly disappear. The code is identical for both lists, does anyone know what may be causing this bug? I created a sample main.dart to demonstrate the effect:
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Reorderable List bug',
home: MainPage(),
);
}
}
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ReorderableListView(
onReorder: (old_index, new_index) {},
children: <Widget>[
textCard(),
textCard2()
],
),
),
Text('Keyboard behaves as expected here but will glitch on following page'),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
child: Text('Go to SubPage'),
onPressed: () {
navigateToSubPage(context);
},
)
],
),
),
);
}
Future navigateToSubPage(context) async {
Navigator.push(context, MaterialPageRoute(builder: (context) => SubPage()));
}
}
class SubPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ReorderableListView(
onReorder: (old_index, new_index) {},
children: <Widget>[
textCard(),
textCard2(),
RaisedButton(
key: ValueKey('3'),
textColor: Colors.white,
color: Colors.redAccent,
child: Text('Back to Main Page'),
onPressed: () {
Navigator.pop(context);
},
)
],
),
);
}
}
Widget textCard() {
return Card(
key: ValueKey('1'),
child: Padding(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 15.0),
child: Text(
'1.',
)),
Expanded(
child: TextFormField(
decoration: InputDecoration(labelText: 'Enter text'),
)
)
],
)));
}
Widget textCard2() {
return Card(
key: ValueKey('2'),
child: TextFormField(
decoration: InputDecoration(
labelText: 'Enter text'
),
),
);
}

Flutter: Floating action button fixed location

I have the following code:
return Scaffold(
appBar: AppBar(
title: Text('Sample Code'),
),
body: ListView(
padding: const EdgeInsets.all(20.0),
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: "Text"),
),
TextField(
decoration: InputDecoration(labelText: "Text"),
),
TextField(
decoration: InputDecoration(labelText: "Text"),
),
],
),
bottomNavigationBar: BottomAppBar(
child: Container(
height: 50.0,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
Whenever the keyboard shows up to enter text into a TextField the FloatingActionButton moves up to the top of the keyboard which will look like this:
What I want is that the button stays in the bottom navigation bar and does not move when the keyboard shows up. I added resizeToAvoidBottomPadding: false, to the Scaffold, which prevents the button from moving but also stops my ListView from moving to stay visible when the keyboard shows up.
Wrapping the floating action button inside a positioned widget did not work for me. However, in combination to adding in your Scaffold the property
resizeToAvoidBottomPadding: false,
You can also use the SingleChildScrollView widget with padding using the MediaQuery.of method...
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.lightBlueAccent,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlueAccent,
child: Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: AddTaskScreen())),
backgroundColor: Colors.transparent);
},
),
Maybe there's a more elegant way of doing this? but this will solve your immediate problem.
There's a nice little plugin to detect the keyboard visibility here.
All you need to do then is to listen to the keyboard visibility state and hide the FAB when the keyboard is visible.
Sample:
import 'package:flutter/material.dart';
import 'package:keyboard_visibility/keyboard_visibility.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SO Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool keyboardOpen = false;
#override
void initState() {
super.initState();
KeyboardVisibilityNotification().addNewListener(
onChange: (bool visible) {
setState(() => keyboardOpen = visible);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample Code'),
),
body: ListView(
padding: const EdgeInsets.all(20.0),
children: <Widget>[
TextField(decoration: InputDecoration(labelText: "Text")),
TextField(decoration: InputDecoration(labelText: "Text")),
TextField(decoration: InputDecoration(labelText: "Text")),
],
),
bottomNavigationBar: BottomAppBar(
child: Container(height: 50.0),
),
floatingActionButton: keyboardOpen
? SizedBox()
: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
Just use the property inside Scaffold
resizeToAvoidBottomInset: false
It will solve the problem immediately..