Flutter: How to get accessibility focus to custom appbar - flutter

If you use the default Material scaffold with the default appbar, and you open your app using TalkBack, the accessibility focus moves automatically to the first item in the appbar (usually, the "Back" button).
I would like my appbar to be taller than the default, so I've created a custom one and pass it to the scaffold:
class CustomAppbar extends StatelessWidget implements PreferredSizeWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 120,
child: Semantics(
focused: true,
child: RaisedButton(
child: Text('Test'),
onPressed: () {},
),
),
);
}
#override
Size get preferredSize => Size.fromHeight(120);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppbar(),
body: SafeArea(
child: Stack(children: <Widget>[
Column(children: <Widget>[
_buildContent(),
]),
]),
),
);
}
The problem is, if I don't use the default AppBar, I can't move the focus to the first button, even if I wrap it with Semantics.
How is the default Scaffold handling the automatic focus? I've searched in the code and couldn't find it.

You can wrap your AppBar with a Semantic node and set focused:true.
Read more here:
https://api.flutter.dev/flutter/semantics/SemanticsProperties/focused.html

So flutter provides a widget PreferredSize to us in which we can override its height and width, I solved my problem in this way
Widget appBar() {
return PreferredSize(
preferredSize: Size.fromHeight(120.0),
child: AppBar(
automaticallyImplyLeading: false,
elevation: 0,
backgroundColor: Colors.blue,
flexibleSpace: SafeArea(
child: Container(
padding: EdgeInsets.only(
left: 16,
right: 16,
top: 16,
bottom: 16
),
child: Container(
// make any type of view
),
),
),
),
);
}

Related

Register tap on empty space around widget

Working on a flutter web project. I have a row which has 3 widgets:
From left to right:
Sidebar
Sidebar content
body
Widget _buildBody() {
final screenwidth = MediaQuery.of(context).size.width;
editpanel = screenwidth * 0.3;
final editor = ViewProvider.of(context).isEditPanelOpen
? (screenwidth - sidebar - editpanel)
: (screenwidth - sidebar);
final ViewProvider viewProvider = Provider.of<ViewProvider>(context);
return Row(
Sidebar()
_loadSidebarContent(bloc.editPanelIndex),
_sidebarHandler(viewProvider),
Center(
child: SizedBox(
width: editor * 0.8,
child: Center(
child: MyWidget(),
),
),
),
],
);
}
I need to register tap if user taps on anything except the Appbar, Sidebar, Sidebarcontent, on MyWidget.
So I wrapped the entire scaffold with gesture detector and tried using IgnorePointer for the specific widgets.
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
.. call some specific function
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize: Size(
MediaQuery.of(context).size.width,
height + 80,
),
child: IgnorePointer(
child: Appbar(),
ignoring: true,
),
),
body: _buildBody(),
),
);
}
Issue is: MyWidget is getting ignored all the time. I don't want to fire the specificFunc() when user taps on any of the: Appbar, Sidebar, Sidebarcontent, or MyWidget.
Basically if user taps the white space around MyWidget specificFunction will be called
Wrap the whole Scaffold widget with GestureDector is not a good idea.
Instead wrap the container (white space around your button) with the detector and supply the button as a child.
In the following sample, the amber area is your white one. Tapping the amber area, and the button produces a separate log.
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
if (kDebugMode) {
print('Amber area tapped!');
}
},
child: Container(
color: Colors.amber,
width: 400,
height: 400,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
style: ButtonStyle(
foregroundColor:
MaterialStateProperty.all<Color>(Colors.blue),
backgroundColor: MaterialStateProperty.all<Color>(
Colors.white)),
onPressed: () {
if (kDebugMode) {
print('Button clicked.');
}
},
child: const Text('A Button'),
),
],
)),
),
)
],
),
),
);
}
}
You could use a stack (https://api.flutter.dev/flutter/widgets/Stack-class.html) and wrap the widget at the very bottom of the stack with a gesture detector.
To position the other widgets correctly, you could use the Positioned widget.
Instead of ignorePointer you should be using AbsorbPointer which will absorb the pointer and not pass it to the content below it

How to make the application responsive using Stack and Positoned - Flutter

How to use Stack and Positioned to add a shape in the SafeArea, I tried to change the color of the AppBar and connect to the shape and add mediaQuery, but still not on every screen it will be properly connected. So how to get a svg photo on the entire surface of SafeArea, and to make it responsive without using appbar, is it necessary to get the effect like in the picture below?(the code gives the effect as in the picture, but it is not responsive and consists of two parts, and I would like one part and get responsive)
Any help very much appreciated.
class Shape extends StatelessWidget {
static Route route() {
return MaterialPageRoute<void>(builder: (_) => Shape());
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
elevation: 0,
actions: [],
),
body: _profilePage(context),
);
}
Widget _profilePage(BuildContext context) {
return SafeArea(
child: Align(
child: Center(
child: Stack(
children: <Widget>[
Positioned(
width: MediaQuery.of(context).size.width * 1,
height: MediaQuery.of(context).size.height,
bottom: MediaQuery.of(context).size.width * 0.6,
child: _curved(context),
),
],
),
),
),
);
}
Widget _curved(BuildContext context) {
return SvgPicture.asset(
'assets/images/shape_purple.svg',
color: Colors.blue,
allowDrawingOutsideViewBox: true,
);
}
Use FitteBox Widget instead
FittedBox(
child: Image.asset('assets/images/background.png'),
fit: BoxFit.fill,
// decoration: BoxDecoration(
// color: Colors.white),
),

onPressed action On Custom Leading Images Appbar in flutter

Is it possible we can add onPressed action on the logo and start another activity?
I am creating a simple flutter app where I have used AppBar and in leading icon I have used a custom logo. I am not sure how to perform onPressed method so that it starts another activity. Anyone please help me here. Below is my app bar code.
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
"assets/images/logo.png",
),
),
title: Text('Safe Outs Business'),
),
body: Center(
child: Text('Admin HomePage'),
),
);
}
}
Click here to see a sample Image of the layout I am trying to build in flutter
You can embed your logo inside a GestureDetector:
return Scaffold(
appBar: AppBar(
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () => print('TAPPED!'),
child: Image.asset(
"assets/images/logo.png",
),
),
),
title: Text('Safe Outs Business'),
),
body: Center(
child: Text('Admin HomePage'),
),
);

How to change appBar size in Scaffold [duplicate]

How can I simply set the height of the AppBar in Flutter?
The title of the bar should be staying centered vertically (in that AppBar).
You can use PreferredSize:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Example',
home: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50.0), // here the desired height
child: AppBar(
// ...
)
),
body: // ...
)
);
}
}
Use toolbarHeight:
There's no longer a need to use PreferredSize. Use toolbarHeight with flexibleSpace.
AppBar(
toolbarHeight: 120, // Set this height
flexibleSpace: Container(
color: Colors.orange,
child: Column(
children: [
Text('1'),
Text('2'),
Text('3'),
Text('4'),
],
),
),
)
You can use PreferredSize and flexibleSpace for it:
appBar: PreferredSize(
preferredSize: Size.fromHeight(100.0),
child: AppBar(
automaticallyImplyLeading: false, // hides leading widget
flexibleSpace: SomeWidget(),
)
),
This way you can keep the elevation of AppBar for keeping its shadow visible and have custom height, which is what I was just looking for. You do have to set the spacing in SomeWidget, though.
The easiest way is to use toolbarHeight property in your AppBar
Example :
AppBar(
title: Text('Flutter is great'),
toolbarHeight: 100,
),
You can add flexibleSpace property in your appBar for more flexibility
Output:
For more controls , Use the PreferedSize widget to create your own appBar
Example :
appBar: PreferredSize(
preferredSize: Size(100, 80), //width and height
// The size the AppBar would prefer if there were no other constraints.
child: SafeArea(
child: Container(
height: 100,
color: Colors.red,
child: Center(child: Text('Fluter is great')),
),
),
),
Don't forget to use a SafeArea widget if you don't have a safeArea
Output :
At the time of writing this, I was not aware of PreferredSize. Cinn's answer is better to achieve this.
You can create your own custom widget with a custom height:
import "package:flutter/material.dart";
class Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Column(children : <Widget>[new CustomAppBar("Custom App Bar"), new Container()],);
}
}
class CustomAppBar extends StatelessWidget {
final String title;
final double barHeight = 50.0; // change this for different heights
CustomAppBar(this.title);
#override
Widget build(BuildContext context) {
final double statusbarHeight = MediaQuery
.of(context)
.padding
.top;
return new Container(
padding: new EdgeInsets.only(top: statusbarHeight),
height: statusbarHeight + barHeight,
child: new Center(
child: new Text(
title,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
),
);
}
}
In addition to #Cinn's answer, you can define a class like this
class MyAppBar extends AppBar with PreferredSizeWidget {
#override
get preferredSize => Size.fromHeight(50);
MyAppBar({Key key, Widget title}) : super(
key: key,
title: title,
// maybe other AppBar properties
);
}
or this way
class MyAppBar extends PreferredSize {
MyAppBar({Key key, Widget title}) : super(
key: key,
preferredSize: Size.fromHeight(50),
child: AppBar(
title: title,
// maybe other AppBar properties
),
);
}
and then use it instead of standard one
You can simply use toolbarHeight, as follows:
appBar: AppBar(
toolbarHeight: 70.0, // add this line
centerTitle: true, // add this line
title: Text('your title'),
),
but if you have any actions the code above doesn't work as you want
you can use this code
AppBar(
centerTitle: true,
title: Padding(
padding: const EdgeInsets.all(16.0),
child: Stack(
alignment: Alignment.center,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Chats', style: TextStyle(color:Colors.black),),
Icon(Icons.add, color: Colors.black,),
],
),
Align(
alignment: Alignment.centerRight,
child: Icon(Icons.add, color: Colors.black,),
),
],
),
),
)
Cinn's answer is great, but there's one thing wrong with it.
The PreferredSize widget will start immediately at the top of the screen, without accounting for the status bar, so some of its height will be shadowed by the status bar's height. This also accounts for the side notches.
The solution: Wrap the preferredSize's child with a SafeArea
appBar: PreferredSize(
//Here is the preferred height.
preferredSize: Size.fromHeight(50.0),
child: SafeArea(
child: AppBar(
flexibleSpace: ...
),
),
),
If you don't wanna use the flexibleSpace property, then there's no need for all that, because the other properties of the AppBar will account for the status bar automatically.
simply use toolbar height ...
AppBar(
title: Text("NASHIR'S APP"),
toolbarHeight: 100,
),
You can use the toolbarHeight property of Appbar, it does exactly what you want.
class AppBarWidget extends StatelessWidget with PreferredSizeWidget {
final String title;
const AppBarWidget({Key? key, required this.title}) : super(key: key);
#override`enter code here`
Widget build(BuildContext context) {
return AppBar(
title: Text(title),
centerTitle: true,
elevation: 0,
actions: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: IconButton(
icon: const FaIcon(FontAwesomeIcons.language),
onPressed: () {},
),
),
],
);
}
#override
Size get preferredSize => const Size.fromHeight(40);// change
}
You can use PreferredSize, by this use can set multiple children widget inside their
appBar: PreferredSize(
preferredSize: Size(MediaQuery.of(context).size.width, 75),
child: Column(children: [
AppBar(
centerTitle: true,
toolbarHeight: 74,
backgroundColor: Colors.white,
elevation: 0,
title: Column(
children: [
Text(
viewModel.headingText,
style: sfDisplay16500Text,
),
SizedBox(
height: 8.0,
),
Text(
viewModel.url.toString(),
style: sfDisplay10400LightBlackText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
],
),
),
]),
),
or just directly use toolbarHeight property for only increase hight of appBar.
appBar: AppBar(
title: Text('AppBar Texr'),
toolbarHeight: 200.0, // double
),
Extend AppBar class and override preferredSize
class AppBarCustom extends AppBar {
#override
Size get preferredSize => Size.fromHeight(100);
}
then use it as you would use AppBar class
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBarCustom(),
body:
),
);
}
}
This is simplest and easiest way to change appbar height without changing original theme.
class AppBarSectionView extends StatefulWidget implements PreferredSizeWidget {
const AppBarSectionView({Key? key}) : super(key: key);
#override
_AppBarSectionViewState createState() => _AppBarSectionViewState();
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight + 20);
}
class _AppBarSectionViewState extends State<AppBarSectionView> {
#override
Widget build(BuildContext context) {
return AppBar(
toolbarHeight: widget.preferredSize.height ,
backgroundColor: Colors.red,
leading: const Icon(Icons.arrow_back_ios_rounded),
title: const Text("This Is Title"),
);
}
}
If you are in Visual Code, Ctrl + Click on AppBar function.
Widget demoPage() {
AppBar appBar = AppBar(
title: Text('Demo'),
);
return Scaffold(
appBar: appBar,
body: /*
page body
*/,
);
}
And edit this piece.
app_bar.dart will open and you can find
preferredSize = new Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
Difference of height!

Flutter Drawer below AppBar

I've implemented a Drawer in my Flutter app.
Closed Drawer:
Opened Drawer:
As you can see, the Drawer is on top of the Appbar. Before I started the app on Flutter, we had a native Android app with a Drawer that used to look like this:
Closed Drawer:
Opened Drawer:
Here is my code:
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return _buildDrawer(context);
}
}
Widget _buildDrawer(BuildContext context) {
return new Drawer(
child: new ListView(
children: <Widget>[
_buildDrawerItem(context, EnumDrawerItem.PROJECT_SELECTION, Icons.home, Colors.transparent),
new Divider(height: 20.0),
_buildDrawerItem(context, EnumDrawerItem.TASK_LIST, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.GUIDED_TASKS, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.PHOTOS, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.DOCUMENTS, Icons.home, Colors.transparent),
new Divider(),
_buildDrawerItem(context, EnumDrawerItem.LOG_OUT, Icons.home, const Color(0x85bf0202)),
new Divider(),
],
),
);
}
Widget _buildDrawerItem(BuildContext context, EnumDrawerItem drawerItem, IconData iconData, Color color) {
return Container(
color: color,
child: new Padding(
padding: new EdgeInsets.all(7.0),
child: new Row(
children: <Widget>[
new Icon(iconData),
new Container(
margin: new EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
child: new Text(
drawerItem.toString(),
style: styleDrawerItem,
),
),
],
),
),
);
}
I know this is the standard Material Design style, but the client wants it as it was before.
Would it be possible to implemented it as in the 2 last screenshots? Do you have any idea?
Wrap your main Scaffold in another Scaffold and use the drawer of child Scaffold also make sure to set automaticallyImplyLeading to false so you don't get back icon in the AppBar
UPDATE :
i don't recommend this way because of this issue
return Scaffold(
primary: true,
appBar: AppBar(
title: Text("Parent Scaffold"),
automaticallyImplyLeading: false,
),
body: Scaffold(
drawer: Drawer(),
),
);
Final Result :
I use the key in scaffold and references in leading in scaffold principal how in the example
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
return Scaffold(
appBar: AppBar(
title: Text('Draw'),
leading: IconButton(
icon: Icon(Icons.dehaze),
onPressed: () {
if (_scaffoldKey.currentState.isDrawerOpen == false) {
_scaffoldKey.currentState.openDrawer();
} else {
_scaffoldKey.currentState.openEndDrawer();
}
})),
body: Scaffold(
key: _scaffoldKey,
drawer: Drawer(),
body: Center(
child: Text('Drawer'),
),
),
);
Try this one:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var statusBarHeight = MediaQuery.of(context).padding.top;
var appBarHeight = kToolbarHeight; //this value comes from constants.dart and equals to 56.0
return Scaffold(
drawerScrimColor: Colors.transparent,
appBar: AppBar(),
drawer: Container(
padding: EdgeInsets.only(top: statusBarHeight+ appBarHeight + 1),//adding one pixel for appbar shadow
width: MediaQuery.of(context).size.width,
child: Drawer(),//write your drawer code
),
body: AnyBody(), //add your body
bottomNavigationBar: AnyNavigationBar(), //add your navigation bar
);
}
}
Simple and to the point:
drawer: Padding(
padding: const EdgeInsets.fromLTRB(0, 80, 0, 0),
child: Drawer(),