Is there way to do the hover menu bar in flutter web?
Already tried to used OverlayEntry and mouse region to detect whether it is hover, but it seems that there is a gap time between move from menu bar to menu item.
Example code for the item:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:overlay_container/overlay_container.dart';
class DropDownMenu extends StatefulWidget {
final String title;
final List<String> route;
final List<String> subTitle;
final BorderRadius borderRadius;
final RenderBox renderBox;
const DropDownMenu({
Key key,
this.title,
this.borderRadius,
this.renderBox,
this.route,
this.subTitle,
}) : super(key: key);
#override
DropDownMenuState createState() => DropDownMenuState();
}
class DropDownMenuState extends State<DropDownMenu> with SingleTickerProviderStateMixin {
GlobalKey _key;
bool isMenuOpen = false;
Offset buttonPosition;
Size buttonSize;
OverlayEntry _overlayEntry;
BorderRadius _borderRadius;
AnimationController _animationController;
RenderBox renderBox;
bool isHover = false;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 100),
);
_borderRadius = widget.borderRadius ?? BorderRadius.circular(4);
_key = LabeledGlobalKey("button_icon");
renderBox = widget.renderBox;
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
void closeMenu() {
_overlayEntry.remove();
_animationController.reverse();
isMenuOpen = !isMenuOpen;
}
void openMenu() {
findButton();
_animationController.forward();
_overlayEntry = _overlayEntryBuilder();
Overlay.of(context).insert(_overlayEntry);
isMenuOpen = !isMenuOpen;
}
findButton() {
RenderBox renderBox = _key.currentContext.findRenderObject();
buttonSize = renderBox.size;
buttonPosition = renderBox.localToGlobal(Offset.zero);
}
#override
Widget build(BuildContext context) {
return Container(
height: 200,
key: _key,
padding: EdgeInsets.only(
left: 20,
),
color: Colors.amberAccent,
child: InkWell(
// onTap: () {
// print('---');
// if (isMenuOpen) {
// closeMenu();
// } else {
// openMenu();
// }
// },
// onHover: (isHover) {
// print(isHover);
// this.isHover = isHover;
// if (isMenuOpen) {
// closeMenu();
// } else {
// openMenu();
// }
// },
child: Column(
children: [
Container(
height: 60,
child: Text(
widget.title ?? '',
style: TextStyle(
color: isHover ? Colors.yellow : Colors.white,
),
),
),
// OverlayContainer(
// show: true,
// // Let's position this overlay to the right of the button.
// position: OverlayContainerPosition(
// // Left position.
// buttonPosition.dx,
// // Bottom position.
// buttonPosition.dy + buttonSize.height - 15,
// ),
// // The content inside the overlay.
// child: Container(
// height: 70,
// padding: const EdgeInsets.all(20),
// margin: const EdgeInsets.only(top: 5),
// decoration: BoxDecoration(
// color: Colors.white,
// boxShadow: <BoxShadow>[
// BoxShadow(
// color: Colors.grey[300],
// blurRadius: 3,
// spreadRadius: 6,
// )
// ],
// ),
// child: Text("I render outside the \nwidget hierarchy."),
// ),
// ),
],
),
),
);
}
OverlayEntry _overlayEntryBuilder() {
return OverlayEntry(
builder: (context) {
return Positioned(
top: buttonPosition.dy + buttonSize.height - 15,
left: buttonPosition.dx,
width: buttonSize.width,
child: Material(
color: Colors.transparent,
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: ClipPath(
clipper: ArrowClipper(),
child: Container(
width: 17,
height: 17,
color: Color(0xFFF),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Container(
height: buttonSize.height * 5 * 10,
decoration: BoxDecoration(
borderRadius: _borderRadius,
),
child: Theme(
data: ThemeData(
iconTheme: IconThemeData(),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: List.generate(widget.subTitle.length, (index) {
return GestureDetector(
onTap: () {
Get.toNamed(widget.route[index] ?? '/');
},
child: Container(
width: buttonSize.width,
height: buttonSize.height + 10,
padding: EdgeInsets.only(bottom: 10),
color: Colors.amberAccent,
child: Center(
child: SelectableText(
widget.subTitle[index] ?? '',
style: TextStyle(
color: Colors.yellow,
),
),
),
),
);
}),
),
),
),
),
],
),
),
);
},
);
}
}
class ArrowClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
Path path = Path();
path.moveTo(0, size.height);
path.lineTo(size.width / 2, size.height / 2);
path.lineTo(size.width, size.height);
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
There is another package example:
https://pub.dev/packages/overlay_container
Related
Hello i am learning the state using the getX methods. When the app loads i am adding the categories in the list but the issue is not updating. can anybody look whats my wrong?
Restaurant Ui Button Page:
HomeController con = Get.put(HomeController());
Widget _cardRestoran(BuildContext context, Restaurants restaurant) {
return GestureDetector(
onTap: () {
con.restoranProducts(context, restaurant);
},
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 15, left: 20, right: 20),
child: ListTile(
title: Text('${restaurant.name ?? ''}' ,),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 5),
Text(
' Şehir: ${restaurant.il ?? ''} - İlçe: ${restaurant.ilce ?? ''}' ,
maxLines: 2,
style: TextStyle(
fontSize: 13
),
),
SizedBox(height: 15),
],
),
trailing: Container(
height: 70,
width: 60,
// padding: EdgeInsets.all(2),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: FadeInImage(
image: restaurant.image != null
? NetworkImage(restaurant.image ?? '')
: AssetImage('assets/img/no-image.png') as ImageProvider,
fit: BoxFit.cover,
fadeInDuration: Duration(milliseconds: 50),
placeholder: AssetImage('assets/img/no-image.png'),
),
),
),
),
),
Divider(height: 1, color: Colors.grey[300], indent: 37, endIndent: 37,)
],
),
);
}
Home Controller
class HomeController extends GetxController{
void restoranProducts(BuildContext context, Restaurants restaurants) async{
await showMaterialModalBottomSheet(
context: context,
useRootNavigator: true,
builder: (context) => RestoranProducts(restaurants: restaurants),
);
}
}
Restaurant Category Controller :
class ClientRestoranProductListController extends GetxController{
String res_id;
List<Category> categories = <Category>[].obs;
ClientRestoranProductListController(#required this.res_id){
getCategories();
}
void getCategories() async {
var result = await categoriesProvider.restaurantCategory("${this.res_id}");
categories.clear();
categories.addAll(result);
}
}
I also tried seperate but it still didnt work.
Restoran Products Category List UI page:
class RestoranProducts extends StatefulWidget {
final Restaurants? restaurants;
const RestoranProducts({Key? key, required this.restaurants}) : super(key: key);
#override
State<RestoranProducts> createState() => _RestoranProductsState();
}
class _RestoranProductsState extends State<RestoranProducts> {
Widget build(BuildContext context) {
String restoranSahibi = "${widget.restaurants?.user_id}";
ClientRestoranProductListController con = Get.put(ClientRestoranProductListController(restoranSahibi));
return Obx(() => DefaultTabController(
length: con.categories.length,
child: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => setState((){
})),
drawer: HomeNavigator(),
appBar: PreferredSize(
preferredSize: Size.fromHeight(110),
child:AppBar(
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors:
[
Colors.amber,
Colors.black,
],
begin: FractionalOffset(0.0, 0.0),
end: FractionalOffset(1.0, 0.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp,
)
),
margin: EdgeInsets.only(top: 15),
alignment: Alignment.topCenter,
child: Wrap(
direction: Axis.horizontal,
children: [
_textFieldSearch(context),
_iconShoppingBag()
],
),
),
centerTitle: true,
iconTheme: IconThemeData(color: Colors.white),
bottom: TabBar(
isScrollable: true,
indicatorColor: Colors.white,
labelColor: Colors.black,
unselectedLabelColor: Colors.white,
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
color: Colors.white
),
tabs: List<Widget>.generate(con.categories.length, (index) {
return Tab(
child: Text(con.categories[index].name ?? ''),
);
}),
),
) ,
),
body: TabBarView(
key: Key(Random().nextDouble().toString()),
children: con.categories.map((Category category) {
return FutureBuilder(
future: con.getProducts(category.id ?? '1', con.productName.value),
builder: (context, AsyncSnapshot<List<Product>> snapshot) {
if (snapshot.hasData) {
if (snapshot.data!.length > 0) {
return ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (_, index) {
return _cardProduct(context, snapshot.data![index]);
}
);
}
else {
return NoDataWidget(text: 'No category');
}
}
else {
return NoDataWidget(text: 'No category');
}
}
);
}).toList(),
)
),
));
}
}
i tried seperate ways but it did not work.
I'm not sure why it can't saveform. Please help me QAQ this is messing up my life
This is the error:
lib/navpages/upload/edit_post_screen.dart:98:65: Error: The argument type 'String' can't be assigned to the parameter type 'AllPosts'.
'AllPosts' is from 'package:pinsta/providers/all_posts.dart' ('lib/providers/all_posts.dart').
onPressed:() => saveForm(i,caption!),
^
import 'package:flutter/material.dart';
import 'package:pinsta/models/post.dart';
import 'package:pinsta/providers/all_posts.dart';
import 'package:provider/provider.dart';
class EditPostScreen extends StatefulWidget {
static String routeName = '/edit-post';
#override
State<EditPostScreen> createState() => _EditPostScreenState();
}
class _EditPostScreenState extends State<EditPostScreen> {
var form = GlobalKey<FormState>();
String? caption?? ;
void saveForm(int i, AllPosts myPosts) {
bool isValid = form.currentState!.validate();
if (isValid) {
form.currentState!.save();
print(caption);
myPosts.updatePost(i,caption);
FocusScope.of(context).unfocus();
form.currentState!.reset();
caption = null;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Flexible(child: Text('updated', style: TextStyle(color: Colors.white, fontSize: 14.5))),
],
),
duration: Duration(seconds: 1),
),
);
}
}
#override
Widget build(BuildContext context) {
AllPosts postList1 = Provider.of <AllPosts> (context);
int i = ModalRoute.of(context)?.settings.arguments as int;
return Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12.0,vertical: 18.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset('assets/images/upload_image.png',height: 200,
width: 200,),
SizedBox(height: 50.0,),
Form(
key: form,
child: Column(
children: [
SizedBox(
height: 60.0,
),
TextFormField(
decoration: InputDecoration(
prefixIcon: Icon(Icons.people, color: Colors.grey),
hintText: 'caption',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24.0)
)
),
validator: (value) {
if (value == null)
return caption;
},
onSaved: (value) {caption = value;},
),
SizedBox(
height: 50.0,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: TextButton(
onPressed:() => saveForm(i,caption!),
child: Text(
'Update', style: TextStyle(fontSize: 17, fontWeight: FontWeight.bold, color: Colors.white),
),
),
)
),
),
],
),
),
],
),
),
),
);
}
}
This is the provider:
import 'package:flutter/material.dart';
import '../models/post.dart';
class AllPosts with ChangeNotifier {
List <Post> myPosts = [];
List<Post> getMyPosts() {
return myPosts;
}
void addPost(imageUrl, caption, username) {
myPosts.insert(0, Post(imageUrl: imageUrl, caption: caption, username:username));
notifyListeners();
}
void removePost(i) {
myPosts.removeAt(i);
notifyListeners();
}
void updatePost(i,caption ) {
myPosts[i].caption = caption;
notifyListeners();
}
}
You method void saveForm(int i, AllPosts myPosts) has AllPosts as the 2nd parameter but you are calling it with saveForm(i,caption!), passing a String (caption) as argument.
I'm relatively new to Flutter and have tried to find similar postings to help me but haven't had any luck in getting them to work for me unfortunately. I have an Android Studio program that's basically a game which involves a grid of flip cards. I have the flip cards in one dart file and the app bar in another. I have an iconbutton on the app bar which currently reduces the point count to zero, but I would also like for it to flip all of the flip cards back to their original positions when pressed. I have a global variable called resetBool that I've been trying to use, something like if resetBool == true then toggleCard() maybe. I think I might need to use a key but am having trouble implementing one properly.
Here is the code in the file which contains my appbar:
import 'package:flip_card/flip_card.dart';
import 'package:flutter/material.dart';
import 'gridone.dart' as gridone;
import 'globalVariables.dart';
import 'statenames.dart';
int count;
StateNames stateObject = new StateNames();
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home>with SingleTickerProviderStateMixin {
TabController controller;
#override
void initState() {
controller = new TabController(length: 1, vsync: this);
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void changeCount() {
setState(() {
counter += 1;
});
}
void decreaseCount() {
setState(() {
counter -= 1;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title:new Text("License plate game"),
backgroundColor: Colors.greenAccent,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.autorenew,
color: Colors.white,
),
onPressed: () {
setState(() {
counter = 0;
resetBool = true;
});
},
),
Center(
child: Container(
padding: EdgeInsets.fromLTRB(20, 20, 20, 20),
child: Text('points: $counter', textAlign: TextAlign.center, style: TextStyle(fontSize: 15),
)
),
),
],
bottom: new TabBar(
controller: controller,
indicatorWeight: 5.0,
indicatorColor: Colors.green,
tabs: <Widget> [
new Tab(icon: new Icon(Icons.image),),
],
),
),
body: new TabBarView(
controller: controller,
children: <Widget>[
new gridone.GridOne(changeCount, decreaseCount),
],
)
);
}
}
And here is the code in the file which contains my flip cards:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flip_card/flip_card.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'statenames.dart';
import 'globalVariables.dart';
import 'Home.dart';
import 'gridtwo.dart' as gridTwo;
StateNames stateObject = new StateNames();
Home homeObject = new Home();
class GridOne extends StatefulWidget {
final Function updateCounter;
final Function decreaseCount;
GridOne(this.updateCounter, this.decreaseCount);
#override
_GridOneState createState() => _GridOneState();
}
class _GridOneState extends State<GridOne>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
int points = 0;
#override
Widget build(BuildContext context) {
super.build(context);
return new Scaffold(
body: new Column(
children: <Widget> [
new Expanded(
child: GridView.count(
crossAxisCount: 5,
children: List.generate(52, (index){
return Card(
elevation: 0.0,
margin: EdgeInsets.only(left: 3.0, right: 3.0, top: 9.0, bottom: 0.0),
color: Color(0x00000000),
child: FlipCard(
direction: FlipDirection.HORIZONTAL,
speed: 1000,
//(resetBool == true) ? cardKey.currentState.toggleCard() : null,
onFlipDone: (status) {
setState(() {
(status)
? widget.decreaseCount()
: widget.updateCounter();
});
if (counter == 25) {
Fluttertoast.showToast(
msg: "You've got 25 states! Wow!",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM_LEFT,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
};
print(counter);
},
front: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FittedBox(fit:BoxFit.fitWidth,
child: Text(stateObject.stateNames[index], style: TextStyle(fontFamily: 'Architects Daughter', color: Colors.white), )
//Theme.of(context).textTheme.headline
),
Text('',
style: Theme.of(context).textTheme.body1),
],
),
),
back: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage(stateObject.licensePlatePaths[index])),
//Text('',
//style: Theme.of(context).textTheme.body1),
],
),
),
),
);
})
)
)
]
),
);
}
}
The solution is to use currentState.toggleCard(); for the cards that are facing Back when the IconButton is clicked.
Basically, what I did is I gave keys to each card at initState method.
List<GlobalKey<FlipCardState>> cardKeys = [];
#override
void initState() {
List.generate(52, (index) {
cardKeys.add(GlobalKey<FlipCardState>());
});
super.initState();
}
Don't forget to put the key to widget
FlipCard(key: cardKeys[index], ... )
Then, call resetCards method below when the button is clicked. If the card is facing back then toggle logic.
void resetCards() {
cardKeys.forEach((element) {
if (!element.currentState.isFront) {
element.currentState.toggleCard();
}
});
setState(() {});
}
You need to call a method on the parent widget, that would be triggered in the child widget. For that, please check this stackoverflow link
Full working code:
import 'package:flutter/material.dart';
import 'package:flip_card/flip_card.dart';
import 'package:fluttertoast/fluttertoast.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(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
// This makes the visual density adapt to the platform that you run
// the app on. For desktop platforms, the controls will be smaller and
// closer together (more dense) than on mobile platforms.
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
TabController controller;
int counter = 0;
final GridOneController myController = GridOneController();
#override
void initState() {
controller = new TabController(length: 1, vsync: this);
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void changeCount() {
setState(() {
counter += 1;
});
}
void decreaseCount() {
setState(() {
counter -= 1;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("License plate game"),
backgroundColor: Colors.greenAccent,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.autorenew,
color: Colors.white,
),
onPressed: () {
setState(() {
counter = 0;
myController.resetCards();
});
},
),
Center(
child: Container(
padding: EdgeInsets.fromLTRB(20, 20, 20, 20),
child: Text(
'points: $counter',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 15),
)),
),
],
bottom: new TabBar(
controller: controller,
indicatorWeight: 5.0,
indicatorColor: Colors.green,
tabs: <Widget>[
new Tab(
icon: new Icon(Icons.image),
),
],
),
),
body: new TabBarView(
controller: controller,
children: <Widget>[
new GridOne(counter, myController),
],
));
}
}
class GridOneController {
void Function() resetCards;
}
class GridOne extends StatefulWidget {
int counter;
final GridOneController controller;
GridOne(this.counter, this.controller);
#override
_GridOneState createState() => _GridOneState(controller);
}
class _GridOneState extends State<GridOne> {
_GridOneState(GridOneController _controller) {
_controller.resetCards = resetCards;
}
int points = 0;
void increaseCounter() {
widget.counter += 1;
}
void decreaseCounter() {
widget.counter -= 1;
}
void resetCards() {
cardKeys.forEach((element) {
if (!element.currentState.isFront) {
element.currentState.toggleCard();
}
});
setState(() {});
}
List<GlobalKey<FlipCardState>> cardKeys = [];
#override
void initState() {
List.generate(52, (index) {
cardKeys.add(GlobalKey<FlipCardState>());
});
super.initState();
}
#override
Widget build(BuildContext context) {
print(cardKeys.length);
return new Scaffold(
body: new Column(children: <Widget>[
new Expanded(
child: GridView.count(
crossAxisCount: 5,
children: List.generate(52, (index) {
return Card(
elevation: 0.0,
margin: EdgeInsets.only(
left: 3.0, right: 3.0, top: 9.0, bottom: 0.0),
color: Color(0x00000000),
child: new FlipCard(
key: cardKeys[index],
direction: FlipDirection.HORIZONTAL,
speed: 1000,
onFlipDone: (status) {
setState(() {
(status) ? decreaseCounter() : increaseCounter();
});
if (widget.counter == 25) {
Fluttertoast.showToast(
msg: "You've got 25 states! Wow!",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM_LEFT,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
}
;
print(widget.counter);
},
front: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FittedBox(
fit: BoxFit.fitWidth,
child: Text(
'FRONT',
style: TextStyle(color: Colors.white),
)
//Theme.of(context).textTheme.headline
),
Text(
'',
),
],
),
),
back: Container(
decoration: BoxDecoration(
color: Color(0xFF006666),
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text('BACK')],
),
),
),
);
})))
]),
);
}
}
I learnt how to make this navigation bar on Youtube for my app but I'm struggling implementing navigation and routing from other tutorials online to the navigation bar that I created.
As you can see below, I've created the bottom navigation bar and called it BottomNavyBar() . As I press each Icon I'd like it to go to a new screen made from a separate dart file to show that each icon goes to a new route.
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Homepage(),
);
}
}
class Homepage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
double xOffset = 0;
double yOffset = 0;
double scaleFactor = 1;
bool isDrawerOpen = false;
#override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(microseconds: 250),
transform: Matrix4.translationValues(xOffset, yOffset, 0)
..scale(scaleFactor),
child: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: Align(
alignment: Alignment.topCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
isDrawerOpen
? IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
setState(() {
xOffset = 0;
yOffset = 0;
scaleFactor = 1;
isDrawerOpen = false;
});
},
)
: IconButton(
icon: Icon(Icons.menu),
onPressed: () {
setState(() {
xOffset = 230;
yOffset = 150;
scaleFactor = 0.6;
isDrawerOpen = true;
});
},
),
Text(
'nghbrly',
style: GoogleFonts.quicksand(
color: const Color(0xff8fc6bb), fontSize: 45),
),
Image(image: AssetImage('assets/images/letterbox.png')),
],
),
),
),
],
),
),
bottomNavigationBar: BottomNavyBar(),
),
);
}
}
class NghbrlySideMenu extends StatefulWidget {
#override
_NghbrlySideMenuState createState() => _NghbrlySideMenuState();
}
class _NghbrlySideMenuState extends State<NghbrlySideMenu> {
#override
Widget build(BuildContext context) {
return Container();
}
}
class BottomNavyBar extends StatefulWidget {
#override
_BottomNavyBarState createState() => _BottomNavyBarState();
}
class _BottomNavyBarState extends State<BottomNavyBar> {
int selectedIndex = 0;
Color backgroundColor = Colors.white;
List<NavigationItem> items = [
NavigationItem(
Icon(
const IconData(0xe904, fontFamily: 'nghbrly'),
),
Text(' Borrow')),
NavigationItem(
Icon(
const IconData(0xe9c8, fontFamily: 'nghbrlyfullicons'),
),
Text(' Lend')),
NavigationItem(
Icon(
const IconData(0xe97a, fontFamily: 'nghbrlyfullicons'),
),
Text(' Favorites')),
NavigationItem(
Icon(
const IconData(0xe996, fontFamily: 'nghbrlyfullicons'),
),
Text(' Nghbrs')),
NavigationItem(
Icon(
const IconData(0xe9cc, fontFamily: 'nghbrlyfullicons'),
),
Text(' Cart')),
];
Widget _buildItem(NavigationItem item, bool isSelected) {
return AnimatedContainer(
duration: Duration(milliseconds: 270),
height: double.maxFinite,
width: isSelected ? 125 : 50,
padding: isSelected
? EdgeInsets.only(left: 16, right: 16)
: EdgeInsets.only(left: 13, right: 8),
decoration: isSelected
? BoxDecoration(
color: const Color(0xff8fc6bb),
borderRadius: BorderRadius.all(Radius.circular(50)))
: null,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
IconTheme(
data: IconThemeData(
size: 24,
color: isSelected ? backgroundColor : const Color(0xff655454),
),
child: item.icon,
),
Padding(
padding: const EdgeInsets.only(left: 5),
child: isSelected
? DefaultTextStyle.merge(
style: TextStyle(
color: backgroundColor,
),
child: item.title)
: Container(),
)
],
)
],
),
);
}
//int _currentIndex = 0;
//final List<Widget> _children = [];
#override
Widget build(BuildContext context) {
return SafeArea(
bottom: true,
child: Container(
height: 56,
padding: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
decoration: BoxDecoration(color: backgroundColor, boxShadow: [
BoxShadow(
color: Colors.white,
blurRadius: 0,
)
]),
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: items.map((item) {
var itemIndex = items.indexOf(item);
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = itemIndex;
});
},
child: _buildItem(item, selectedIndex == itemIndex),
);
}).toList(),
),
),
);
}
}
Another thing that I'd like to find out if possible is, what if I wanted to place the navigation bar below the app heading "nghbrly" like in this picture . I'd really appreciate it.
use this plugin Presistant_bottom_nav_bar. you can use the bottomnav bar in every pages.also you can disable the bottomnav in specific screen checkout above link
it very good plugin in my opinion.checkout navgation style in this link.you can change the desgin of bottomnavbar navBarStyle: NavBarStyle.style9, just change the style9 to whatever number provided by plugin. i believe 15 of them available
You can use your custom icon instead of default icon, also instead of this CupertinoColors.systemPurple you can also use Colors.red kind of this Let me know if it works
PersistentTabController _controller =PersistentTabController(initialIndex: 0);
//Screens for each nav items.
List<Widget> _NavScreens() {
return [
Screen1(),
Screen2(),
Screen3(),
Screen4(),
];
}
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
icon: Icon(Icons.home),
title: ("Borrow"),
activeColor: CupertinoColors.activeBlue,
inactiveColor: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.favorite),
title: ("Lend"),
activeColor: CupertinoColors.activeGreen,
inactiveColor: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.person_pin),
title: ("Favorites"),
activeColor: CupertinoColors.systemRed,
inactiveColor: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.local_activity),
title: ("Cart"),
activeColor: CupertinoColors.systemIndigo,
inactiveColor: CupertinoColors.systemGrey,
),
];
}
#override
Widget build(BuildContext context) {
return Center(
child: PersistentTabView(
controller: _controller,
screens: _NavScreens(),
items: _navBarsItems(),
confineInSafeArea: true,
backgroundColor: Colors.white,
handleAndroidBackButtonPress: true,
resizeToAvoidBottomInset: true,
hideNavigationBarWhenKeyboardShows: true,
decoration: NavBarDecoration(
borderRadius: BorderRadius.circular(10.0),
),
popAllScreensOnTapOfSelectedTab: true,
navBarStyle: NavBarStyle.style9,
),
);
}
How can I make a Card in Flutter that overlaps the AppBar? Negative margins are not possible as far as I know.
See the image for clarity.
For one card it could be easily done with Stack widget
E.g.
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
Home({Key key}) : super(key: key);
#override
HomeState createState() {
return new HomeState();
}
}
class HomeState extends State<Home> {
bool _hasCard;
#override
void initState() {
super.initState();
_hasCard = false;
}
#override
Widget build(BuildContext context) {
List<Widget> children = new List();
children.add(_buildBackground());
if (_hasCard) children.add(_buildCard());
return MaterialApp(
home: Stack(
children: children,
),
);
}
void _showCard() {
setState(() => _hasCard = true);
}
void _hideCard() {
setState(() => _hasCard = false);
}
Widget _buildCard() => new Container(
child: new Center(
child: new Container(
height: 700.0,
width: 200.0,
color: Colors.lightBlue,
child: new Center(
child: new Text("Card"),
),
),
),
);
Widget _buildBackground() => new Scaffold(
appBar: new AppBar(
title: new Text("AppBar"),
),
body: new Container(
child: _hasCard
? new FlatButton(
onPressed: _hideCard, child: new Text("Hide card"))
: new FlatButton(
onPressed: _showCard, child: new Text("Show card")),
),
);
}
void main() {
runApp(
new Home(),
);
}
If there are many cards, you can wrap them into ListView.
class Sample2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Material(
child: CustomScrollView(
slivers: [
SliverPersistentHeader(
delegate: MySliverAppBar(expandedHeight: 200),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => ListTile(
title: Text("Index: $index"),
),
),
)
],
),
),
);
}
}
class MySliverAppBar extends SliverPersistentHeaderDelegate {
final double expandedHeight;
MySliverAppBar({#required this.expandedHeight});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: [
Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
fit: BoxFit.cover,
),
Center(
child: Opacity(
opacity: shrinkOffset / expandedHeight,
child: Text(
"MySliverAppBar",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 23,
),
),
),
),
Positioned(
top: expandedHeight / 2 - shrinkOffset,
left: MediaQuery.of(context).size.width / 4,
child: Opacity(
opacity: (1 - shrinkOffset / expandedHeight),
child: Card(
elevation: 10,
child: SizedBox(
height: expandedHeight,
width: MediaQuery.of(context).size.width / 2,
child: FlutterLogo(),
),
),
),
),
],
);
}
#override
double get maxExtent => expandedHeight;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}