Flutter: Reuse of AppBar widget - flutter

I created several screens, for some reasons I have to individually create a Scaffold which represents the screen. However, as the AppBar should be everytime the same, I thought of create it once in a stateless widget and the just reuse this:
import 'package:flutter/material.dart';
class MyAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppBar(
centerTitle: true,
backgroundColor: Colors.black,
title: Text(
"Places Near You",
style: TextStyle(
color: Colors.black, fontFamily: "Billabong", fontSize: 35),
),
);
}
}
and then on every Screen i wanting to use this by writting:
class _CreatePostScreenState extends State<CreatePostScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
body: Center(
child: Text("Hello"),
));
}
}
However, I get the following error which I dont know how to solve (I imported everything correctly):

Your app bar must implement PreferredSizeWidget.
class YourAppbar extends StatelessWidget implements PreferredSizeWidget {
#override
Widget build(BuildContext context) {
return AppBar();
}
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

For me the following solution went fine too:
class YourAppBar extends AppBar {
YourAppBar({Key key, Widget title})
: super(key: key, title: title, actions: <Widget>[
new IconButton(
onPressed: (){},
icon: new Icon(Icons.access_alarm)),
]);
}
And can be used as follow:
return Scaffold(
appBar: YourAppBar(
title: Text('Hi'),
),
body: Center(
child: Text('Home page'),
),
);

Related

Flutter: hide navbar when drawer is open

I followed this tutorial on how to create a bottom navbar and it works great apart from the fact that I dont know how to appropriately add a drawer.
Currently my code looks something like this for the screen that holds the navigational bar:
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async =>
!await navigatorKeys[currentTab].currentState.maybePop(),
child: Scaffold(
body: Stack(children: <Widget>[
_buildOffstageNavigator(TabItem.red),
_buildOffstageNavigator(TabItem.green),
_buildOffstageNavigator(TabItem.blue),
]),
bottomNavigationBar: BottomNavigation(
currentTab: currentTab,
onSelectTab: _selectTab,
),
),
);
}
And like this for my home screen:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
endDrawer: const NavigationDrawerWidget(),
body: const _HomePageBody(),
);
}
}
All of this is functioning, however, the drawer does not hide the navigational bar when its opened. I have thought about placing the drawer instead on the screen that holds the but that introduces more problems than it fixes. Such as the hamburger icon does not appear unless I also add an appbar to the said screen and I only want it to be present on the homepage and not its "subpages" (pages that I can access through the homepage but are not the pages present on the navbar).
My next thought is that I could possibly hide the navbar when the drawer opens and reveal it when the drawer is closed again. But then there is the difficulty of animating in such a way to make it look nice and seems like a long winded solution.
At this point it seems like the navbar is the problem but I have tried redoing it so many times such that the end result would be a bar that is present on all pages (+ subpages) and saves state that it would be unfortunate to change it up again.
I would appreciate any suggestions or links to other projects/tutorials that are doing something similar. Thanks :)
Edit
here is a reproducible example, I wipped it up quite quickly so sorry for it being a bit messy
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int pageIndex = 0;
final pages = [
const Page(
title: "page 1",
drawer: true,
),
const Page(
title: "page 2",
drawer: false,
),
const Page(
title: "page 3",
drawer: false,
),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: pages[pageIndex],
bottomNavigationBar: Container(
color: Theme.of(context).primaryColor,
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(
onPressed: () {
setState(() {
pageIndex = 0;
});
},
icon: const Icon(
Icons.home_outlined,
),
),
IconButton(
onPressed: () {
setState(() {
pageIndex = 1;
});
},
icon: const Icon(
Icons.work_outline_outlined,
),
),
IconButton(
onPressed: () {
setState(() {
pageIndex = 2;
});
},
icon: const Icon(
Icons.widgets_outlined,
),
),
],
),
),
);
}
}
class Page extends StatelessWidget {
const Page({Key? key, required this.title, required this.drawer})
: super(key: key);
final String title;
final bool drawer;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
endDrawer: drawer ? const Drawer() : null,
body: Center(
child: Text(
title,
style: const TextStyle(
fontSize: 45,
fontWeight: FontWeight.w500,
),
),
),
);
}
}
I think you can use single scaffold and include your drawer and appBar on 1st scaffold.
return Scaffold(
endDrawer: pageIndex == 0 ? const Drawer() : null,
body: pages[pageIndex],
appBar: AppBar(),
bottomNavigationBar: Container(

How do I move the list to the center of the screen?

I'm going through the first codelab for flutter. I have no experience with coding and am new to flutter. I'm trying to play around with modifying the given code but everything I type returns a million errors. How do I adjust the positioning of the infinite scroll list?
import 'package:english_words/english_words.dart';
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: 'Name generator',
home: Scaffold(
appBar: AppBar(
title: const Text('name generator'),
),
body: const Center(
child: RandomWords(),
),
),
);
}
}
class RandomWords extends StatefulWidget {
const RandomWords({Key? key}) : super(key: key);
#override
State<RandomWords> createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18);
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if (i.isOdd) return const Divider();
final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return ListTile(
title: Text(
_suggestions[index].asPascalCase,
style: _biggerFont
),
);
},
);
}
}
I figured the way was to add body: const Center( right above the padding for the list but it didn't work, and I don't know where to put it.
Mock up of centering text
You can wrap your Text to make it center.
return ListTile(
title: Center(
child: Text(_suggestions[index].asPascalCase, style: _biggerFont),
),
);
return ListTile(
title: Text(
_suggestions[index].asPascalCase,
style: _biggerFont,
textAlign: TextAlign.center,
),
);
I am not sure what have you tried, but you in order to center the title of the ListTile you can use a center widget like you did in your code, or wrap your text within a Row widget and set mainAxisAlignment: MainAxisAlignment.center.
your ListTile widget takes a full-screen width widget so you can directly align text to the center in Text Widget.
return ListTile(
title: Text(
_suggestions[index].asPascalCase,
style: _biggerFont,
textAlign: TextAlign.center,
),
);

How to draw expandable and collapsible sidebar menu in Flutter?

Though I am new to Flutter but now on my way of cloning an expandable and collapsible sidebar menu of a website i saw on the internet.
My attempt
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final appTitle = 'Drawer Demo';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Text('Dashboard')),
drawer: Drawer(
child: ListView(
children: <Widget>[
Text('Dashboard'),
ExpansionTile(
title: Text("User management"),
children: <Widget>[Text("Users"),
Text("Add user"),
Text("Migrate users")
],
),
ExpansionTile(
title: Text("Remittance management"),
children: <Widget>[Text("Add remittance"),
Text("Trace remittance"),
Text("Remittance history"),
Text("Search remittances"),
Text("Online remittance requests")
],
)
],
),
),
);
}
}
Code output
Required output
Dear members, any help ?
Thanks in advance.
Use ExpansionPanelList() to wrap your ExpansionPanels
Even though i wanted to implement ExpansionTitle widget but this alternative widget mentioned above made me search for it and found this GitHub link below implementing ExpansionPanelList widget in the way i wanted.
Here is the link:
https://github.com/jaggerwang/flutter-in-practice

Flutter Web persistent header

With the introduction of flutter for web it has me trying to achieve a website style header that is persistent when using routes and across the entire app. Appbar doesn't appear to be the solution since each scaffold has its own appBar. I've created the header widget that's in a Column with the MaterialApp. However, this implementation feels wrong as everything should be a child of MaterialApp or CupertinoApp.
If the searchBar header can be placed within the MaterialApp and I'm able to use Navigator that's would be preferred. I'm really here for guidance and the "right" way to do this.
void main() {
initKiwi();
// BlocSupervisor().delegate = AppBlocDelegate();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Material(
elevation: 2.0,
color: Colors.white,
child: MediaQuery(
data: MediaQueryData.fromWindow(ui.window),
child: Directionality(
textDirection: TextDirection.ltr,
child: Container(
height: 50,
child: SearchBar(),
),
),
),
),
Expanded(
child: MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl',
),
home: Text("Pages & Routes Here"),
),
),
]);
}
}
Though it's not using routes I was able to solve this using IndexedStack. This also preserves any scrolling I've done in the ProductsPage() when closing the search page. The AppBar is persistent and was able to keep the code to a minimum.
main.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl'
),
home: MainPage(),
);
}
}
main_page.dart
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final _searchBloc = kiwi.Container().resolve<SearchBloc>();
final _productsBloc = kiwi.Container().resolve<ProductsBloc>();
PageController pageController;
int currentPage = 0;
void _onSearchActive({bool isActive}) {
setState(() {
this.currentPage = isActive ? 1 : 0;
});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: _buildScaffold(),
);
}
Widget _buildScaffold() {
return BlocProviderTree(
blocProviders: [
BlocProvider<SearchBloc>(bloc: _searchBloc),
BlocProvider<ProductsBloc>(bloc: _productsBloc),
],
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: SearchBar(onIsActive: _onSearchActive),
),
body: IndexedStack(
children: [
ProductsPage(),
SearchPage(),
],
index: currentPage,
),
),
);
}
}

The AppBarDesign can't be assigned to the parameter type 'PreferredSizeWidget'

Anyone please give some information why this is happening?
When I try to add a class AppBarDesign which implements appBar flutter is giving the below error.
error: The argument type 'AppBarDesign' can't be assigned to the parameter type 'PreferredSizeWidget'. (argument_type_not_assignable at [flutterbyrajath] lib\main.dart:27)
import 'package:flutter/material.dart';
main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Rajath\'s design ',
debugShowCheckedModeBanner: false,
theme: new ThemeData(primarySwatch: Colors.amber),
home: new MyHomePage(key, 'App is Fun'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage(Key key, this.title) : super(key: key);
final title;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBarDesign(key, title),
);
}
}
class AppBarDesign extends StatelessWidget {
AppBarDesign(Key key, this.title) : super(key: key);
final title;
#override
Widget build(BuildContext context) {
return new AppBar(
title: new Text(title),
);
}
}
helpful tips to implementing that without searching any other topics:
class ApplicationToolbar extends StatelessWidget with PreferredSizeWidget{
#override
Widget build(BuildContext context) {
return AppBar( ... );
}
#override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}
Scaffold requires as appbar a class that implements PreferredSizeWidget
Either wrap your custom appbar into a PreferredSize
Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(100),
child: Container(color: Colors.red),
),
)
or implement PreferredSizeWidget:
Scaffold(
appBar: MyAppBar(),
)
...
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
#override
Size get preferredSize => const Size.fromHeight(100);
#override
Widget build(BuildContext context) {
return Container(color: Colors.red);
}
}
Screenshot:
Create this class:
class CustomAppBar extends PreferredSize {
#override
final Widget child;
final double height;
CustomAppBar({
required this.height,
required this.child,
}) : super(child: child, preferredSize: Size.fromHeight(height));
}
Usage:
appBar: CustomAppBar(
height: 100,
child: Container(
color: Colors.red,
child: Column(
children: [
Text('0'),
Text('1'),
Text('2'),
Text('3'),
],
),
),
)
You can also use following way to design your appbar in separate file and then use it in your whole app.
Widget Custom_Appbar(){
return AppBar(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(20))),
title: Text(
'Decimal to Binary & Vice Versa',
style: TextStyle(fontWeight: FontWeight.w400, fontSize: 19),
));
}
And then use it like this
return Scaffold(
appBar: Custom_Appbar(),
)
If you get the error
The argument type x can't be assigned to the parameter type
'PreferredSizeWidget'.
Just wrap x in the PreferredSize widget.
Example:
appBar: AppBar(
bottom: Column(
children: <Widget>[
new TabBar(
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(
icon: new Icon(Icons.airplanemode_active),
)
],
),
],
),
Here I get the error: The argument type 'Column' can't be assigned to the parameter type 'PreferredSizeWidget'.
Solution:
Click on Column
Click on light bulb
Choose Wrap with Widget
Replace widget with PreferredSize
Add a PreferredSize attribute, such as preferredSize: Size.fromHeight(100.0),
Result:
appBar: AppBar(
bottom: PreferredSize(
preferredSize: Size.fromHeight(100.0),
child: Column(
children: <Widget>[
new TabBar(
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(
icon: new Icon(Icons.airplanemode_active),
)
],
),
],
),
),
First:
implement your class
implements PreferredSizeWidget
Second:
Enter Height
#override
Size get preferredSize => Size.fromHeight(AppBar().preferredSize.height);