I am trying to design a WhatsApp-like menu from BottomNavigationBar for one of my applications which is written in flutter.
I have tried designing with PopupMenuButton class but it will cover the BottomNavigation also.
I need a popup like menu which is above the BottomNavigationBar something like below
the PopupMenuButton opens up a menu just above the button so it blocked the bottom bar. I found a workaround, instead of using PopupMenuButton, try using a BottomAppBar with the IconButton as one of the children, then use showBottomSheetso it won't block the BottomAppBar.
Note: Most of my codes are just placeholders to mimic the layout of your image, you need to rewrite most of them to match your needs. The key is using showBottomSheet for your problem.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
bottomNavigationBar: BottomAppBar(
child: Container(
height: 70,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Icon(Icons.home),
Icon(Icons.alarm),
MyMiddleIcon(),
Icon(Icons.album),
Icon(Icons.assignment_ind),
],
),
),
shape: CircularNotchedRectangle(),
color: Colors.blueGrey[50],
),
);
}
}
class MyMiddleIcon extends StatelessWidget {
#override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.add),
onPressed: () {
showBottomSheet(
context: context,
builder: (context) => Container(
color: Colors.red[100],
height: 250,
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Icon(Icons.home),
Icon(Icons.hot_tub),
Icon(Icons.hourglass_empty),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Icon(Icons.home),
Icon(Icons.hot_tub),
Icon(Icons.hourglass_empty),
],
),
],
),
));
});
}
}
Related
I need to put a divider in a Column inside a Row but the divider does not appear. I saw some questions about the Row should not be used but in my case, I need the Row. Here is the simplest code, I need the divider to be shown
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: const [
Text('up'),
Divider(
color: Colors.black,
thickness: 2,
),
Text('bottom'),
],
),
],
),
),
);
}
}
To get the divider to show, you need to warp your Divider() with a SizedBox() and define a set width. As well as removing const form the Column() since we're using MediaQuery:
SizedBox(
width: MediaQuery.of(context).size.width,
child: Divider(
color: Colors.black,
thickness: 2,
),
),
Result:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Text('up'),
SizedBox(
width: MediaQuery.of(context).size.width,
child: Divider(
color: Colors.black,
thickness: 2,
),
),
Text('bottom'),
],
),
],
),
),
);
}
}
While the structure is
Center
- Row
- Column
- Text
- Divider
- Text
This divider doesn't get any constrained and the result is invisible on UI.
Wrapping Divider with SizedBox provide the constraints, and we are able to see the divider. Also you can go for Expanded on Column widget.
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Column(
children: const [
Text('up'),
Divider(
color: Colors.green,
thickness: 2,
),
Text('bottom'),
],
),
),
],
),
),
//main.dart file
import 'package:flutter/material.dart';
import 'package:messenger/widgets/splash_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
appBarTheme: AppBarTheme(
brightness: Brightness.light,
color: Colors.white,
elevation: 0.0,
iconTheme: IconThemeData(color: Colors.black),
actionsIconTheme: IconThemeData(color: Colors.black)),
fontFamily: "Ubuntu",
primarySwatch: Colors.blue,
primaryColor: Color(0xff0084FF),
),
home: Column(
children: <Widget>[
SizedBox(height: 30.0,),
ElevatedButton(
onPressed: () => navigation(context),
child: Text("to splash screen"))
],
),
);
}
navigation(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(builder: (context)=>SplashScreen()));
}
}
//SplashScreen.dart file
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:messenger/widgets/login_button.dart';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
Widget build(BuildContext context) {
double imageSize = MediaQuery.of(context).size.width / 3;
return Scaffold(
body: Center(
child: Image.asset(
"images/logo.png",
width: imageSize,
height: imageSize,
fit: BoxFit.cover,
),
),
bottomNavigationBar: Padding(
padding: EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 16.0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
LoginButton(text: "Continue with Facebook",onPressed: (){
},
colour: Color(0xff3b5998),
leading: Image.asset("images/facebook-logo.png",
),
),
],
),
),
);
}
}
Navigator operation requested with a context that does not include a Navigator.
Maybe I didn't understand the concept of context well that's why I am getting the
error.Why is this error happening? The second widget that is SplashScreen has
context. But still I am getting the error. Is there any problem in the code that I
missed?
bro you its not good way to call Navigator.push in Navigator please try to call in specific function or you can call this in ink well so please remove
the **Navigator(BuildContext context)**after thats its will work fine for you.
**here is the code that can solve your navigation problem **
enter code here
import 'package:flutter/material.dart';
import 'screen/splashScreen.dart';
class DemoNavigation extends StatefulWidget {
const DemoNavigation({Key? key}) : super(key: key);
#override
_DemoNavigationState createState() => _DemoNavigationState();
}
class _DemoNavigationState extends State<DemoNavigation> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Column(
children: [
Text("Navigator Demo"),
ElevatedButton(
onPressed: () {
// here is the function that your are using
navigation(context);
},
child: Text("TO Next Screen"),
),
],
),
),
);
}
/// here you need to change the context that your are passing to build
/// copy this
void navigation(BuildContext context) {
Navigator.push(context, MaterialPageRoute(builder: (_) =>
SplashScreen()));
}
}
I'm trying to make an expandable button, a bit like the expandable fab, except it's not a fab as it is not floating. This is the expandable fab for perspective:
What I'm trying to achieve though is to have a self contained button that expands above it with a menu. Self contained is in bold because I'd like the widget to be used easily without having to modify the parents structure.
So if you copy paste the code below in dartpad you'll see a yellow bar at the bottom. However if you uncomment the lines which are commented, which represents the menu expanding, you'll see that the bottom bar is pushed to the top.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: [
Expanded(child: Container(color: Colors.purple)),
MyWidget(),
]
),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SizedOverflowBox(
size: Size(double.infinity, 100),
child: Stack(
children: [
Container(color: Colors.amber, height: 100),
// Transform.translate(
// offset: Offset(0, -400),
// child: Container(color: Colors.lightBlue, height: 400, width: 80),
// ),
]
)
);
}
}
So my questions are:
How do I achieve the required result where the bottom bar does not move and a menu above it (light blue container); modifying only MyWidget and not MyApp ?
Why in the current code the bar is pushed above ?
Overlay and OverlayEntry can help to achieve this:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: [
Expanded(child: Container(color: Colors.purple)),
MyWidget(),
]
),
),
),
);
}
}
class MyWidget extends StatelessWidget {
OverlayEntry? _overlayEntry;
_hideMenu() {
_overlayEntry?.remove();
}
_showMenu(BuildContext context) {
final overlay = Overlay.of(context);
_overlayEntry = OverlayEntry(
builder: (ctx) => Stack(
children: [
GestureDetector(
onTap: () => _hideMenu(),
child: Container(color: Colors.grey.withAlpha(100)),
),
Positioned(
bottom: 100,
left: 50,
child: Container(color: Colors.pink, height: 200, width: 50,),
),
],
)
);
overlay?.insert(_overlayEntry!);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _showMenu(context),
child: Container(color: Colors.amber, height: 100)
);
}
}
Try this, run this code in dartpad.
It contains one parent, three child which can be called using the menu buttons,
The FloatingActionButton.extended used in this code can be replaced by any custom Widget, you can give onTap methods for clicks,
I have used simple widgets, Let me know wether you were looking for something like that, or something different.
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'I am Parent'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool showButtons = false;
var index = 0;
List<Widget> childList = [Child1(), Child2(), Child3()];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: childList[index],
),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: [
Visibility(
visible: showButtons,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton.extended(
heroTag: 'btn1',
onPressed: () {
setState(() {
index = 0;
});
},
label: Text(
"Sub Btn1",
style: TextStyle(color: Colors.black),
),
elevation: 3,
backgroundColor: Colors.yellowAccent,
),
Padding(
padding: EdgeInsets.only(top: 3),
child: FloatingActionButton.extended(
heroTag: 'btn1',
onPressed: () {
setState(() {
index = 1;
});
},
label: Text(
"Sub Btn2",
style: TextStyle(color: Colors.black),
),
elevation: 3,
backgroundColor: Colors.yellowAccent,
)),
Padding(
padding: EdgeInsets.only(top: 3),
child: FloatingActionButton.extended(
heroTag: 'btn3',
onPressed: () {
setState(() {
index = 2;
});
},
label: Text(
"Sub Btn3",
style: TextStyle(color: Colors.black),
),
elevation: 3,
backgroundColor: Colors.yellowAccent,
))
],
),
),
RaisedButton(
onPressed: () {
setState(() {
showButtons = !showButtons;
});
},
child: Text("Self Contained"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
color: Colors.yellow,
),
],
) // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class Child1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("I am Child 1"),
);
}
}
class Child2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("I am Child 2"),
);
}
}
class Child3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("I am Child 3"),
);
}
}
I have a SelectableText and Icon inside a Row, but SelectableText does not seem to be properly aligned.
Right now it looks like this,
Expected Result:
This is code I am using,
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(FontAwesomeIcons.whatsapp),
const Padding(padding: EdgeInsets.only(left: 8)),
SelectableText(
'+91-8**2**8**5',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.title,
)
],
),
PS: It works well with Text but I want SelectableText instead.
Issue Selectabletext widget on web is cutted on half
https://github.com/flutter/flutter/issues/40663 has been solved.
please upgrade to latest version
attached my full test code and result
full test code
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(FontAwesomeIcons.whatsapp),
const Padding(padding: EdgeInsets.only(left: 8)),
SelectableText(
'+91-8**2**8**5',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.title,
)
],
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I am looking for a way to style the clipboard actions without touching textTheme/button property of the main style theme. Is this even possible?
Seems like the style is directly tied to the theme. Not the best idea but if you really wanted to you would need to create a custom popup and handle all the actions yourself.
This should get you started...
Output:
Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _controller = new TextEditingController();
final _textfieldFocusNode = new FocusNode();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: GestureDetector(
// intercept all pointer calls
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(_textfieldFocusNode);
},
onLongPress: () {
showMenu(
context: context,
// TODO: Position dynamically based on cursor or textfield
position: RelativeRect.fromLTRB(0.0, 300.0, 300.0, 0.0),
items: [
PopupMenuItem(
child: Row(
children: <Widget>[
// TODO: Dynamic items / handle click
PopupMenuItem(
child: Text(
"Paste",
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Colors.red),
),
),
PopupMenuItem(
child: Text("Select All"),
),
],
),
),
],
);
},
child: IgnorePointer(
// ensures textfield doesn't overrule GestureDetector
child: TextField(
focusNode: _textfieldFocusNode,
controller: _controller,
),
),
),
)
],
),
),
);
}
}