i'm trying to make an appbar as a class for one of my page of my app (only used on 1 page).
I'd like to have addStoryAppBar for my code to be easier to read. How do I do this ? I've tried to create a widget, but it remove the leading back icon
class StoryAddPage extends StatefulWidget {
const StoryAddPage({Key key}) : super(key: key);
#override
_StoryAddPageState createState() => _StoryAddPageState();
}
class _StoryAddPageState extends State<StoryAddPage> {
#override
Widget build(BuildContext context) {
AppBar addStoryAppBar = AppBar(
backgroundColor: Colors.white,
title: Text(
AppLocalizations.of(context).add_story,
style: TextStyle(
color: AppColors.Black,
fontWeight: FontWeight.w700,
fontSize: 16),
),
leading: SvgPicture.asset(
"lib/assets/images/back.svg",
semanticsLabel: 'Back icon',
fit: BoxFit.none,
height: 10,
),
actions: [
GestureDetector(
child: Image.asset('lib/assets/images/select_picture.png'),
onTap: () => {},
),
Padding(
padding: const EdgeInsets.all(10.0),
child: ElevatedButton(
style: kOrangeButton,
onPressed: () => {},
child: Container(
child: Text(
AppLocalizations.of(context).publier,
style: TextStyle(color: AppColors.Black),
),
),
),
),
],
);
return SafeArea(
child: Scaffold(
appBar: addStoryAppBar,
body: Container(
child: Text('Add story'),
),
),
);
}
}
Also tried to extends the AppBar, but how do I pass the context ? Is this the more adapted thing to do ?
class StoryAppBar extends AppBar {
StoryAppBar()
: super(
iconTheme: IconThemeData(
color: Colors.black, //change your color here
),
backgroundColor: Colors.white,
title: Text(
AppLocalizations.of(context).add_story,
style: TextStyle(
color: AppColors.Black,
fontWeight: FontWeight.w700,
fontSize: 16),
),
elevation: 0.0,
automaticallyImplyLeading: false,
actions: <Widget>[
IconButton(
icon: Icon(Icons.notifications),
onPressed: () => null,
),
IconButton(
icon: Icon(Icons.person),
onPressed: () => null,
),
],
);
}
You can extract the AppBar widget to a statefull or stateless widget.
Create your own leading back icon. Just create Text Button, like this:
TextButton.icon(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back_rounded),
label: Text(''),
),
Create a separate method that returns AppBar to your screen widget. Add the below method in a new class and call this method from anywhere you want to show AppBar
AppBar getApplicationAppBar(BuildContext context) {
return AppBar(
backgroundColor: Colors.white,
title: Text(
AppLocalizations
.of(context)
.add_story,
style: TextStyle(
color: AppColors.Black,
fontWeight: FontWeight.w700,
fontSize: 16),
),
leading: SvgPicture.asset(
"lib/assets/images/back.svg",
semanticsLabel: 'Back icon',
fit: BoxFit.none,
height: 10,
),
actions: [
GestureDetector(
child: Image.asset('lib/assets/images/select_picture.png'),
onTap: () => {},
),
Padding(
padding: const EdgeInsets.all(10.0),
child: ElevatedButton(
style: kOrangeButton,
onPressed: () => {},
child: Container(
child: Text(
AppLocalizations
.of(context)
.publier,
style: TextStyle(color: AppColors.Black),
),
),
),
),
],
);
}
If back button functionality not works, then use GestureDetector on Back Button
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: SvgPicture.asset(
"lib/assets/images/back.svg",
semanticsLabel: 'Back icon',
fit: BoxFit.none,
height: 10,
),
),
if you have stateless widget you can Easily implement PreferredSizeWidget
this example show you how do that :
class CustomAppbar extends StatelessWidget implements
PreferredSizeWidget{
const CustomAppbar({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container();
}
#override
Size get preferredSize => Size(15.h, 100.w);
}
if you are using StatefulWidget you can copy paste this code
class CustomAppbar extends StatefulWidget implements PreferredSizeWidget {
final bool? isBack;
final TextEditingController? controller;
const CustomAppbar({Key? key, this.isBack, this.controller})
: super(key: key);
#override
State<CustomAppbar> createState() => _CustomAppbarState();
#override
Size get preferredSize => Size(15.h, 100.w);
}
class _CustomAppbarState extends State<CustomAppbar> {
#override
Widget build(BuildContext context) {
return Container()
}
}
and now you can use class on appbar
Scaffold(
appBar: CustomAppbar(
);
the rason for that if you enter to appbar you will see
/// An app bar to display at the top of the scaffold.
final PreferredSizeWidget? appBar;
the app bar is PreferredSizeWidget
and PreferredSizeWidget implement Widget Class
which contain get method
Size get preferredSize;
Related
I am lacking experience in Flutter to understand how to pass a callback funciton into a Widget:
Custom button :
class IconButtonWidget extends StatelessWidget {
final Future<void> callback;
const IconButtonWidget({Key? key, required this.callback}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextButton.icon(
icon: const Icon(
Icons.add_alarm,
size: 30,
),
label: const Text(
'New alarm',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
letterSpacing: 1.0),
),
onPressed: () async {
await callback;
},
);
}
}
I have a simple Home Screen where I use it (see commented out line):
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Center(
child: IconButtonWidget(
// callback: () {Navigator.pushNamed(context, '/alarms');},
),
),
SizedBox(
height: 20.0,
),
],
)),
);
}
}
I want to pass a function to the Widget, but don't understand how i can do that.
Previously it was used like this (before I separated the widget in a separate class)
child: TextButton.icon(
icon: const Icon(
Icons.add_alarm,
size: 30,
// color: Colors.teal,
),
label: const Text(
'New alarm',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
letterSpacing: 1.0),
),
onPressed: () async {
dynamic result =
await Navigator.pushNamed(context, '/alarms');
},
),
),
Please refer to below code
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: IconButtonWidget(callback: () async {
print("Call back Function");
}),
),
),
);
}
}
class IconButtonWidget extends StatelessWidget {
final Function callback;
const IconButtonWidget({Key? key, required this.callback}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextButton.icon(
icon: const Icon(
Icons.add_alarm,
size: 30,
),
label: const Text(
'New alarm',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20, letterSpacing: 1.0),
),
onPressed: () {
callback();
},
);
}
}
class IconButtonWidget extends StatelessWidget {
final VoidCallback onPressed;
const IconButtonWidget({Key? key, required this.onPressed}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextButton.icon(
icon: const Icon(
Icons.add_alarm,
size: 30,
),
label: const Text(
'New alarm',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
letterSpacing: 1.0),
),
onPressed: onPressed,
);
}
}
.........
Center(
child: IconButtonWidget(
onPressed: () async {
dynamic result =
await Navigator.pushNamed(context, '/alarms');
},
),
),
.........
I'm a bit of a newb with Flutter and Dart, and Google couldn't help me with this question.
Say I have this:
class _MapPageState extends BaseState<MapPage> with SingleTickerProviderStateMixin
I want to add another mixin (BasePage) on top of this that contains a reusable appbar, drawer, etc. The layout is described in this article.
I know this is not possible, and my IDE throws an error that tells me to integrate them, but I do not know how. Is there a solution? I need SingleTickerProviderStateMixin because it is required by an AnimationController I am using.
Here is the custom mixin code, if needed:
abstract class Base extends StatefulWidget {
Base({Key key}) : super(key: key);
}
abstract class BaseState<Page extends Base> extends State<Page> {
String screenName();
}
mixin BasePage<Page extends Base> on BaseState<Page> {
MapFunctions functions = MapFunctions();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Guidey'),
backgroundColor: Colors.deepOrangeAccent,
centerTitle: true,
),
drawer: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.black.withOpacity(0.5)
),
child: Drawer(
child: ListView(
padding: EdgeInsets.fromLTRB(40.0, 10.0, 40.0, 10.0),
children: <Widget>[
DrawerHeader(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 35, 0, 0),
child: Text('Navigation', textAlign: TextAlign.center, style: TextStyle(fontSize: 20, color: Colors.white))
)
),
ListTile(
title: Text('Profile', style: TextStyle(color: Colors.white)),
trailing: Icon(Icons.account_circle, color: Colors.white70),
onTap: (){
Navigator.of(context).pop();
},
),
ListTile(
title: Text('Map', style: TextStyle(color: Colors.white)),
trailing: Icon(Icons.drive_eta, color: Colors.white70),
onTap: (){
Navigator.of(context).pop();
},
),
ListTile(
title: Text('My Location', style: TextStyle(color: Colors.white)),
trailing: Icon(Icons.store, color: Colors.white70),
onTap: (){
},
)
],
)
),
),
);
}
Widget body();
}
It turns out I was thinking too big, combining mixins can be done with a simple comma.
... with SingleTickProviderMixin, BasePageMixin
While creating the theme:
appBarTheme: new AppBarTheme(
iconTheme: base.appBarTheme.iconTheme.copyWith(size: 100, color: Colors.red),
//Color is working but size having no effect*
),
The easiest way to change your hamburger menu is by changing your leading widget from the AppBar
final _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.menu, size: 40), // change this size and style
onPressed: () => _scaffoldKey.currentState.openDrawer(),
),
title: Text('Home Screen'),
),
body: Container(),
drawer: Container(
width: 300,
color: Colors.white,
),
);
}
This change will create this huge hamburger menu:
import 'dart:math';
import 'package:app_with_bloc/util/MyScreenUtil.dart';
import 'package:flutter/material.dart';
class AppBar extends StatefulWidget implements PreferredSizeWidget {
GlobalKey scaffoldKey;
// AppBar(this.scaffoldKey, {Key key}) : preferredSize = Size.fromHeight(kToolbarHeight), super(key: key);
AppBar(this.scaffoldKey, {Key key})
: preferredSize = Size.fromHeight(max(MyScreenUtil().getScaledSize(kToolbarHeight), kToolbarHeight)),
super(key: key);
#override
final Size preferredSize; // default is 56.0
#override
_AppBarState createState() => _AppBarState();
}
class _AppBarState extends State {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: SafeArea(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [
Padding(
padding: EdgeInsets.only(bottom: MyScreenUtil().getScaledSize(4.0), left: MyScreenUtil().getScaledSize(4.0), ),
child: IconButton(
icon: Icon(Icons.menu, size: MyScreenUtil().getScaledSize(26), color: Colors.white), // change this size and style
onPressed: () => widget.scaffoldKey.currentState.openDrawer(),
),
),
Padding(
padding: EdgeInsets.all(MyScreenUtil().getScaledSize(8.0)),
child: Text('Start', style: Theme.of(context).textTheme.title.apply(fontSizeFactor: 1.4, color: Colors.white)),
),
Padding(
padding: EdgeInsets.only(bottom: MyScreenUtil().getScaledSize(4.0), right: MyScreenUtil().getScaledSize(4.0), ),
child: IconButton(
icon: Icon(Icons.menu, size: MyScreenUtil().getScaledSize(26), color: Colors.white),
),
),
]
),
),
);
}
}
I'm trying to change the color of the raisedButton when pressed, I have been having trouble with this for a while, the code I have right now for the whole button widget is
children: <Widget>[
Expanded(
child: Container(
child: new SizedBox(
width: 15,
height: 60,
child: new RaisedButton(
color: pressed ? Color(0xFF1D1E33) : Colors.blue,
hoverColor: Colors.blueAccent,
focusColor: Colors.blueAccent,
child: Text(
'Male',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
onPressed: () => setState((){pressed = !pressed;})),
),
I also had a boolean as shown below but I received an error when running the code saying "Failed assertion: boolean expression must not be null"
bool get pressed => null;
set pressed(bool pressed) {}
You can copy paste run full code below
code snippet
bool _pressed = false;
bool get pressed {
return _pressed;
}
set pressed(bool pressed) {
_pressed = pressed;
}
working demo
full code
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> {
int _counter = 0;
bool _pressed = false;
bool get pressed {
return _pressed;
}
set pressed(bool pressed) {
_pressed = pressed;
}
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>[
Container(
child: new SizedBox(
width: 100,
height: 60,
child: new RaisedButton(
color: pressed ? Color(0xFF1D1E33) : Colors.blue,
hoverColor: Colors.blueAccent,
focusColor: Colors.blueAccent,
child: Text(
'Male',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
onPressed: () => setState(() {
pressed = !pressed;
})))),
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 making a flutter application that have shopping cart. Right now, i am just working on a single functionality of it when we select a particular product, after increase or decrease its quantity then our cart doesnot show the quantity of that item on the cart immediately. we have to go to another widget then it update the count on cart.
Note: HomeScreen comprise of two stateful widget, one is bottom navigation bar which have cart and other icons along with other icons and their respective UI's and other one is Product screen which is showing all our products, and in my product screen i used listview and in its UI i used - and + icons to increase or decrease its quantity. I am sharing the code of the widget - and +(a small portion of product screen) on which i want to implement this functionality.
This is the video link to show
https://youtu.be/3qqVpmWguys
HomeScreen:
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
//static _HomeScreenState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<_HomeScreenState>());
int _currentindex = 0;
var cart;
final List<Widget> children = [
ProductScreen(),
OrderScreen(),
CartScreen(),
AccountScreen(),
];
List<BottomNavigationBarItem> _buildNavigationItems() {
var bloc = Provider.of<CartManager>(context);
int totalCount = bloc.getCart().length;
setState(() {
totalCount;
});
return <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.reorder,
size: 30,
color: Colors.white,
),
title: Text(
'Product',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.add_alert,
size: 30,
color: Colors.white,
),
title: Text(
'Order',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
BottomNavigationBarItem(
icon: Stack(
children: <Widget>[
Icon(
Icons.shopping_cart,
size: 30,
color: Colors.white,
),
Positioned(
bottom: 12.0,
right: 0.0,
child: Container(
constraints: BoxConstraints(
minWidth: 20.0,
minHeight: 20.0,
),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10.0),
),
child: Center(
child: Text(
'$totalCount',
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
)
],
),
title: Text(
'Cart',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.lock,
size: 30,
color: Colors.white,
),
title: Text(
'Account',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
];
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomPadding: true,
body: children[_currentindex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.transparent,
backgroundColor: Colors.orange,
onTap: onNavigationTapbar,
currentIndex: _currentindex,
items: _buildNavigationItems(),
type: BottomNavigationBarType.fixed,
),
),
);
}
void onNavigationTapbar(int index) {
setState(() {
_currentindex = index;
});
}
}
ProductScreen incrementor or decrementor:
class TEProductIncrementor extends StatefulWidget {
var product;
TEProductIncrementor({
this.product,
});
#override
_TEProductIncrementorState createState() => new _TEProductIncrementorState();
}
class _TEProductIncrementorState extends State<TEProductIncrementor> {
int totalCount = 0;
#override
Widget build(BuildContext context) {
var cartmanager = CartManager();
void decrementsavecallback() {
// bloc.decreaseToCart(widget.ctlist);
//CartManager().updateToCart(totalCount.toString(), widget.ctlist);
setState(() {
if (totalCount > 0) {
totalCount--;
cartmanager.updateToCart(totalCount.toString(),widget.product);
}
});
}
void increasesavecallback() {
setState(() {
totalCount++;
cartmanager.updateToCart(totalCount.toString(),widget.product);
});
}
return Container(
margin: EdgeInsets.only(top: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), color: Colors.orange),
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
iconSize: 30,
icon: new Icon(
Icons.remove,
),
onPressed: () {
decrementsavecallback();
},
),
Text(
totalCount.toString(),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
IconButton(
iconSize: 30,
icon: new Icon(Icons.add),
onPressed: () {
increasesavecallback();
},
)
],
),
),
);
}
}
You can pass the entire function into a widget just implemented like below
class Parent extends StatefulWidget {
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
#override
Widget build(BuildContext context) {
return Button(
(){
setState(() {
///define your logic here
});
}
);
}
}
class Button extends StatelessWidget {
final Function onTap;
Button(this.onTap);
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: onTap,
);
}
}
But if your project is pretty big then I will recommend you to use any state management libraries just like redux or mobx.
https://pub.dev/packages/mobx
You can use BLoC pattern for this,
Here is full answer and demo code that you can check.
Why not use keys?
Assign at both widget a GlobalKey and then, when you need to update that state you just need to call
yourGlobalKeyName.currentState.setState((){
//your code
});