Related
I have a flutter app that is complex enough and i have been developing it for a while.
the problem is after finishing up with the design code , i started implementing API's to receive actual data and populate the app with it. the app is now crashing quite a lot ( although a little less in release mode).
I am thinking it might be because the android device runs out of memory because of all the complex widgets I display in the app.
I have build all of my widgets Stateful I don't know if that could be a related factor (I am a beginner with flutter programming).
Please I have spent a lot of time in developing this app and it's my first enterprise level flutter app.
I haven't had experience with flutter before and I knew this kind of problems would arise and have no solution I would have gone with Java and Swift to develop the app.
Any help is appreciated guys. Thanks.
the app only displays about a 30-40 images, I don't know where all the data coming from.
Here is the code :
import 'package:flutter/material.dart';
import 'package:bestiee/componenets/constants.dart';
import 'package:bestiee/componenets/cards/single-item-hot-screen-card.dart';
import 'package:bestiee/componenets/buttons/small-round-more-button.dart';
import 'package:bestiee/componenets/cards/single-place-hot-screen-card.dart';
import 'package:bestiee/componenets/cards/single-person-hot-screen-card.dart';
import 'package:bestiee/componenets/cards/single-place-large-hot-screen-card.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-items-screen.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-places-screen.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-people-screen.dart';
import 'package:bestiee/translations.dart';
import 'package:bestiee/models/models.dart';
import 'package:bestiee/networking.dart';
import 'package:bestiee/constants.dart';
import 'dart:convert';
import 'package:http/http.dart';
class HotScreen extends StatefulWidget {
static List<Item> items = [];
static List<Place> places = [];
static List<Person> people = [];
static bool isFirstRun = true;
#override
_HotScreenState createState() => _HotScreenState();
}
class _HotScreenState extends State<HotScreen> {
String getTranslation(String text) {
try {
String translation = Translations.of(context).text(text);
return translation;
} catch (ex) {
print(ex);
return '';
}
}
getAllPlaces() async {
HotScreen.places.removeRange(0, HotScreen.places.length);
Response response = await getRequest(devBaseURL + plainPlaceAPI);
var allPlacesMap = jsonDecode(response.body);
print(allPlacesMap.length);
for (int i = 0; i < allPlacesMap.length; i++) {
var json = allPlacesMap[i];
Place place = Place.fromJson(json);
setState(() {
HotScreen.places.add(place);
});
}
}
getAllItems() async {
HotScreen.items.removeRange(0, HotScreen.items.length);
Response response = await getRequest(devBaseURL + plainItemAPI);
var allItemsMap = jsonDecode(response.body);
for (int i = 0; i < allItemsMap.length; i++) {
var json = allItemsMap[i];
Item item = Item.fromJson(json);
setState(() {
HotScreen.items.add(item);
});
}
}
getAllPeople() async {
HotScreen.people.removeRange(0, HotScreen.people.length);
Response response = await getRequest(devBaseURL + plainPersonAPI);
var allPeopleMap = jsonDecode(response.body);
for (int i = 0; i < allPeopleMap.length; i++) {
var json = allPeopleMap[i];
Person person = Person.fromJson(json);
setState(() {
HotScreen.people.add(person);
});
}
}
#override
void initState() {
super.initState();
if(HotScreen.isFirstRun == true){
getAllItems();
getAllPlaces();
getAllPeople();
HotScreen.isFirstRun = false;
}
}
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
body: Container(
decoration: kPageMainBackgroundColorBoxDecoration,
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
//main screen scrollable widgets
child: ListView(
shrinkWrap: true,
children: <Widget>[
Text(
getTranslation('Bestiee'),
style: kBazarGalleryTitleStyle,
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text('Featured Items', style: kFeatureTitleTextStyle),
),
Container(
height: 700 / 3.5,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: HotScreen.items.length,
itemBuilder: (contet, int index) {
return SingleItemCard(
item: HotScreen.items[index],
isPersonItem: false,
moduleName: HotScreen.items[index].moduleName,
);
}),
),
SizedBox(
height: 10,
),
//more button
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(onPressed: () {
Navigator.pushNamed(context, MoreHotItemsScreen.id);
}),
),
),
SizedBox(
height: 20,
),
//second hand section
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text('Second Hand', style: kFeatureTitleTextStyle),
),
// Container(
// height: 700 / 3.5,
// child: ListView.builder(
// scrollDirection: Axis.horizontal,
// shrinkWrap: true,
// itemCount: 9,
// itemBuilder: (contet, int index) {
// return SingleItemCard();
// }),
// ),
SizedBox(
height: 10,
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(
onPressed: () {
Navigator.pushNamed(context, MoreHotItemsScreen.id);
},
),
),
),
SizedBox(
height: 30,
),
//places section
Text(
'Featured Plces',
style: kFeatureTitleTextStyle,
),
Container(
height: 140,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: HotScreen.places.length,
itemBuilder: (context, int index) {
return Container(
width: 190,
height: 100,
child: SinglePlaceCard(
place: HotScreen.places[index],
));
},
),
),
//
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(
onPressed: () {
Navigator.pushNamed(context, MoreHotPlacesScreen.id);
},
),
),
),
SizedBox(
height: 30,
),
//people section
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Text(
'People',
style: kFeatureTitleTextStyle,
),
),
Container(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: HotScreen.people.length,
itemBuilder: (context, int index) {
return Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SinglePersonHotScreenCard(
person: HotScreen.people[index],
),
),
);
},
),
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(
onPressed: () {
Navigator.pushNamed(context, MoreHotPeopleScreen.id);
},
),
),
),
SizedBox(
height: 60,
),
//single shops section
Container(
height: 100.0 * 3,
child: Column(
children: List.generate(5, (index) {
return Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SinglePlaceLargeHotCard(place: HotScreen.places[index],)
),
);
}),
)
),
],
),
),
),
),
);
}
}
code for SingleItemCard :
class SingleItemCard extends StatelessWidget {
SingleItemCard({
this.cardHeight = 300,
this.cardWidth = 200,
this.color = kAppOrangeColor,
this.isExtraInfoShowen = true,
this.item,
this.isPersonItem,
this.moduleName});
final double cardHeight;
final double cardWidth;
final Color color;
final bool isExtraInfoShowen;
final Item item;
final bool isPersonItem;
final String moduleName;
#override
Widget build(BuildContext context) {
String imageURL ;
//remove the null part in production
if(item.moduleName == 'item' || item.moduleName == null ){
imageURL = imageBaseURLPlaces + item.imageVariants[0].imageURL;
}else if (item.moduleName == 'person' ){
imageURL = imageBaseURLPeople + item.imageVariants[0].imageURL;
print(imageURL);
}
int ratingUserCount = item.ratingUserCount;
return FittedBox(
child: Padding(
padding: const EdgeInsets.only(right: 7),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SingleItemScreen(
item: item,
),
),
);
},
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
//upper item description
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Visibility(
visible: isExtraInfoShowen,
child: CircleRater(
isRatingDisabled: true,
rating: item.rating,
ratingUserCount: item.ratingUserCount != null ? ratingUserCount : 0,
),
),
SizedBox(
width: 150,
child: Text(
// 'Item Name Here no. 1',
item.name,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 15,
color: Colors.black,
fontWeight: FontWeight.w500),
softWrap: false,
),
),
//item card
Container(
height: cardHeight,
width: cardWidth,
child: Container(
width: 100,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
// child: Image.asset(
// 'images/item-image1.jpeg',
// width: 180,
// fit: BoxFit.fill,
// ),
child: CachedNetworkImage(imageUrl: imageURL, fit: BoxFit.fill,)
),
),
),
],
),
//lower price widget
Visibility(
visible: isExtraInfoShowen,
child: Align(
alignment: Alignment.bottomLeft,
child: Container(
width: 100,
height: 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(10),
topLeft: Radius.circular(5)),
color: color),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 3, horizontal: 4),
child: Align(
alignment: Alignment.bottomRight,
child: Text(
// '1000000 IQD',
item.priceIQD.toString() + ' IQD',
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
textAlign: TextAlign.start,
),
),
),
),
),
)
],
),
),
),
);
}
}
SinglePlaceCard :
class SinglePlaceCard extends StatelessWidget {
SinglePlaceCard({this.place});
Place place;
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double widgetWidth = screenWidth / 2.3;
int ratingUserCount = place.ratingUserCount;
String imageURL = place.coverImages != null
? imageBaseURLPlaces +
place.coverImages[0].imageURL
: '';
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SinglePlaceScreen(place: place, isScreenCalledFromOwnerSelfRegistrationScreen: false,),
),
);
},
child: FittedBox(
child: Padding(
padding: const EdgeInsets.only(right: 10),
child: Container(
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
Column(
//upper place name and rating
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CircleRater(
isRatingDisabled: true,
rating: place.rating,
ratingUserCount: place.ratingUserCount != null ? ratingUserCount : 0,
),
SizedBox(
width: widgetWidth,
child: Text(
place.name,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500),
softWrap: false,
),
),
//place card image
Container(
height: 80,
width: 170,
child: Container(
width: 150,
decoration: BoxDecoration(
color: kAppMainDarkGreenColor,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: place.coverImages == null
? Image.asset(
'images/blank-placeholder-images/blank-image-placeholder.png',
width: screenWidth / 4,
fit: BoxFit.fill,
)
: CachedNetworkImage(
imageUrl: imageURL,
fit: BoxFit.fill,
),
),
),
),
],
),
//lower tag
Align(
alignment: Alignment.bottomLeft,
child: Container(
width: 100,
height: 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(3),
bottomRight: Radius.circular(8)),
color: Colors.black54),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 3, horizontal: 4),
child: Align(
alignment: Alignment.bottomRight,
child: FittedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
EyeIconWithText(
size: 13,
),
Padding(
padding: const EdgeInsets.only(right: 5),
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
color: place.isOnline != null &&
place.isOnline == true
? Colors.green
: Colors.red),
),
),
Text(
place.isOpen != null
? place.isOpen ? 'Open Now' : 'Colosed Now'
: 'Closed Now',
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
textAlign: TextAlign.start,
),
],
),
),
),
),
),
)
],
),
),
),
),
);
}
}
SinglePersonHotScreenCard :
class SinglePersonHotScreenCard extends StatelessWidget {
SinglePersonHotScreenCard(
{ this.isExtraInfoShowen = false, this.person});
final bool isExtraInfoShowen;
final Person person;
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
final ratingPersonCount = person.ratingUserCount;
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, SinglePersonScreen.id);
},
child: FittedBox(
child: Column(
children: <Widget>[
//upper label and rating
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CircleRater(
isRatingDisabled: true,
rating: person.rating,
ratingUserCount: person.ratingUserCount != null ? ratingPersonCount : 0,
),
Text(
person.name,
style: TextStyle(color: Colors.black),
),
],
),
Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.all(Radius.circular(1000)),
),
width: screenWidth / 3.6,
height: screenWidth / 3.6,
),
Visibility(
visible: person != null,
child: CircleAvatar(
radius: 50,
backgroundImage: NetworkImage(imageBaseURLPeople + person.profileImageURL,)
),
),
Visibility(
visible: person == null,
child: ClipOval(
child: SizedBox(
width: screenWidth / 4,
child:
Image.asset('images/blank-placeholder-images/person-placeholder.jpg'),
),
),
),
],
),
Text(
person.jobTitle,
style: TextStyle(color: Colors.black),
),
Padding(
padding: const EdgeInsets.only(top: 2.0),
child: Container(
width: 15,
height: 15,
decoration: kOnlineOfflineIndicatorBoxDecoration,
),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: Visibility(
visible: this.isExtraInfoShowen,
child: ThumbsUpWithLabel(),
),
)
],
),
),
),
);
}
}
I have ran the devtool, the app uses too much m memory, I am not sure why.
I changed the code there is a lot less crashes. I use this code
getAllPlaces() async {
List<Place> places = widget.places;
places.removeRange(0, places.length);
List<Place> _places = [];
Response response = await getRequest(devBaseURL + plainPlaceAPI);
var allPlacesMap = jsonDecode(response.body);
for (int i = 0; i < allPlacesMap.length; i++) {
var json = allPlacesMap[i];
Place place = Place.fromJson(json);
_places.add(place);
}
setState(() {
places.addAll(_places );
print('all added');
});
}
The app crash seems to be caused by Out Of Memory issue. Checking on the snippets you've provided, memory issues were caused by nested ListViews rendering Widgets that displays images. While the widgets in ListViews are only rendered when near the viewport, multiple ListViews still adds up to the total memory consumed. I suggest trimming down the images displayed and nest ListViews sparingly to prevent these issues.
I've a Material widget to wrap a MaterialButton to make border radius, but I can't set the width attribute to it. I tried use a SizedBox but not works, the Material widget keep using all space of screen.
Code:
return new SizedBox(
width: 40,
child: Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22.0)),
elevation: 18.0,
color: Color(0xFF801E48),
clipBehavior: Clip.antiAlias,
child: MaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
height: 30.0,
child: new Text('Sair',
style:
new TextStyle(fontSize: 16.0, color: Colors.white)),
),
),
);
Result:
Clearly it not have 40.0 of width size.
A better approach is using Container widget. When you need to change width, height or add padding/margin to any widget you should wrap the target widget into a container. The container widget is for this kind of job.
Container(
width: myWidthValue, // Container child widget will get this width value
height: myHeghtValue, // Container child widget will get this height value
padding: allowPaddingToo, // padding is allowed too
child: Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22.0)),
elevation: 18.0,
color: Color(0xFF801E48),
clipBehavior: Clip.antiAlias, // Add This
child: MaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
height: 30.0,
child: new Text('Sair',
style:
new TextStyle(fontSize: 16.0, color: Colors.white)),
onPressed: () {
setState(() {
_isNeedHelp = false;
});
},
),
),
);
This is how you can set the size, padding, and margin of widgets in general. here is an example for Button:
Container(
margin: EdgeInsets.only(top: 10), // Top Margin
child:
ElevatedButton(
style: TextButton.styleFrom(
// Inside Padding
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20),
// Width,Height
minimumSize: Size(300, 30),
),
child: Text('Upload Data'),
onPressed: () {submitForm();},
),
),
Solved using Padding:
return Padding(
padding: EdgeInsets.fromLTRB(50, 0, 50, 0),
child: Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22.0)),
elevation: 18.0,
color: Color(0xFF801E48),
clipBehavior: Clip.antiAlias, // Add This
child: MaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
height: 30.0,
child: new Text('Sair',
style:
new TextStyle(fontSize: 16.0, color: Colors.white)),
onPressed: () {
setState(() {
_isNeedHelp = false;
});
},
),
),
);
Result:
I want to create a popup menu when clicking on a button from the appbar .. i want something like this to appear:
is there a way to do this in flutter? a package or something?
I tried, but I've faced some problems with showing subwidget exactly this way. So, here two solutions:
class TestScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> with SingleTickerProviderStateMixin {
AnimationController animationController;
bool _menuShown = false;
#override
void initState() {
animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
super.initState();
}
#override
Widget build(BuildContext context) {
Animation opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(animationController);
if (_menuShown)
animationController.forward();
else
animationController.reverse();
return Scaffold(
appBar: AppBar(
actions: <Widget>[IconButton(icon: Icon(Icons.menu), onPressed: (){
setState(() {
_menuShown = !_menuShown;
});
})],
),
body: Stack(
overflow: Overflow.visible,
children: <Widget>[
Positioned(
child: FadeTransition(
opacity: opacityAnimation,
child: _ShapedWidget(),
),
right: 4.0,
top: 16.0,
),
],
),
);
}
}
class _ShapedWidget extends StatelessWidget {
_ShapedWidget();
final double padding = 4.0;
#override
Widget build(BuildContext context) {
return Center(
child: Material(
clipBehavior: Clip.antiAlias,
shape:
_ShapedWidgetBorder(borderRadius: BorderRadius.all(Radius.circular(padding)), padding: padding),
elevation: 4.0,
child: Container(
padding: EdgeInsets.all(padding).copyWith(bottom: padding * 2),
child: SizedBox(width: 150.0, height: 250.0, child: Center(child: Text('ShapedWidget'),),),
)),
);
}
}
class _ShapedWidgetBorder extends RoundedRectangleBorder {
_ShapedWidgetBorder({
#required this.padding,
side = BorderSide.none,
borderRadius = BorderRadius.zero,
}) : super(side: side, borderRadius: borderRadius);
final double padding;
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return Path()
..moveTo(rect.width - 8.0 , rect.top)
..lineTo(rect.width - 20.0, rect.top - 16.0)
..lineTo(rect.width - 32.0, rect.top)
..addRRect(borderRadius
.resolve(textDirection)
.toRRect(Rect.fromLTWH(rect.left, rect.top, rect.width, rect.height - padding)));
}
}
In this case subwidget is below appbar
class TestScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> with SingleTickerProviderStateMixin {
AnimationController animationController;
bool _menuShown = false;
#override
void initState() {
animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
super.initState();
}
#override
Widget build(BuildContext context) {
Animation opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(animationController);
if (_menuShown)
animationController.forward();
else
animationController.reverse();
return Scaffold(
appBar: AppBar(
elevation: 0.0,
actions: <Widget>[Stack(
overflow: Overflow.visible,
children: <Widget>[IconButton(icon: Icon(Icons.menu), onPressed: (){
setState(() {
_menuShown = !_menuShown;
});
}),
Positioned(
child: FadeTransition(
opacity: opacityAnimation,
child: _ShapedWidget(onlyTop: true,),
),
right: 4.0,
top: 48.0,
),
],)],
),
body: Stack(
overflow: Overflow.visible,
children: <Widget>[
Positioned(
child: FadeTransition(
opacity: opacityAnimation,
child: _ShapedWidget(),
),
right: 4.0,
top: -4.0,
),
],
),
);
}
}
class _ShapedWidget extends StatelessWidget {
_ShapedWidget({this.onlyTop = false});
final double padding = 4.0;
final bool onlyTop;
#override
Widget build(BuildContext context) {
return Center(
child: Material(
clipBehavior: Clip.antiAlias,
shape:
_ShapedWidgetBorder(borderRadius: BorderRadius.all(Radius.circular(padding)), padding: padding),
elevation: 4.0,
child: Container(
padding: EdgeInsets.all(padding).copyWith(bottom: padding * 2),
child: onlyTop ? SizedBox(width: 150.0, height: 20.0,) : SizedBox(width: 150.0, height: 250.0, child: Center(child: Text('ShapedWidget'),),),
)),
);
}
}
class _ShapedWidgetBorder extends RoundedRectangleBorder {
_ShapedWidgetBorder({
#required this.padding,
side = BorderSide.none,
borderRadius = BorderRadius.zero,
}) : super(side: side, borderRadius: borderRadius);
final double padding;
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return Path()
..moveTo(rect.width - 8.0 , rect.top)
..lineTo(rect.width - 20.0, rect.top - 16.0)
..lineTo(rect.width - 32.0, rect.top)
..addRRect(borderRadius
.resolve(textDirection)
.toRRect(Rect.fromLTWH(rect.left, rect.top, rect.width, rect.height - padding)));
}
}
In this case top of subwidget is on appbar, but appbar has to have 0.0 elevation
Actually, both of these solutions are not complete in my opinion, but it can help you to find what you need
It might be too late for an answer. But this can be simply achieved by using OverlayEntry widget. We create a widget of that shape and pass it to OverlayEntry widget and then use Overlay.of(context).insert(overlayEntry) to show the overlay and overlayEntry.remove method to remove it.
Here is a medium link to create a Custom DropDown Menu
Hope this helps!
There is a package called flutter_portal which works like Overlay/OverlayEntry but in a declarative way. You can use it for implementing custom tooltips, context menus, or dialogs.
CustomPopupMenu(
pressType: PressType.singleClick,
controller: menu,
arrowColor: AppColor.white,
menuBuilder: () => ClipRect(
clipBehavior: Clip.hardEdge,
child: Container(
height: MediaQuery.of(context).size.height *
ComponentSize.container1height,
width: MediaQuery.of(context).size.width *
ComponentSize.conatiner1width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
ComponentSize.borderradius),
color: AppColor.white,
),
child: ListView.builder(
itemCount: Details.length,
itemBuilder: (context, index) {
return Column(
children: [
InkWell(
onTap: () {
do somthing
},
child: Column(
children: [
Container(
padding: EdgeInsets.only(
left:
ComponentSize.paddingleft),
alignment: Alignment.centerLeft,
child: Text(
Details[index],
style: const TextStyle(
color: Colors.black,
fontFamily: 'Taml_001'),
textAlign: TextAlign.start,
),
),
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(
left:
ComponentSize.paddingleft),
child: Text(Details[index],
style: TextStyle(
color: AppColor.black
.withOpacity(
ComponentSize
.opacity1),
fontSize: ComponentSize
.containerfontsize)),
)
],
),
),
const Divider(),
],
);
},
),
)),
child: Container(
color: AppColor.white,
padding: EdgeInsets.only(
top: ComponentSize.paddingbottom,
bottom: ComponentSize.paddingtop,
left: ComponentSize.padding1left),
width: ComponentSize.container2width,
height: ComponentSize.container2height,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: ComponentSize.textcontainerwidth,
height: ComponentSize.textcontainerheight,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Text(
Tamil,
style: const TextStyle(
color: Colors.black,
fontFamily: 'Taml_001'),
),
),
),
SizedBox(
width: ComponentSize.textcontainerwidth,
height: ComponentSize.textcontainerheight,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Text(
English,
style: const TextStyle(
color: Colors.black),
),
),
)
],
),
),
SizedBox(
child: Icon(
Icons.expand_more,
size: ComponentSize.iconarrowsize,
color: Colors.black,
),
)
],
),
),
),
Is there any way to show fullscreen image ?
var imagejadwal = new Image.network(
"https://firebasestorage.googleapis.com/v0/b/c-smp-bruder.appspot.com/o/fotojadwal.jpg?alt=media&token=b35b74df-eb40-4978-8039-2f1ff2565a57",
fit: BoxFit.cover
);
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: imagejadwal
),
);
in that code, there's space around the image :/
Your problem is that Center will make the image to get it's preferred size instead of the full size.
The correct approach would be instead to force the image to expand.
return new Scaffold(
body: new Image.network(
"https://cdn.pixabay.com/photo/2017/02/21/21/13/unicorn-2087450_1280.png",
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
alignment: Alignment.center,
),
);
The alignment: Alignment.center is unnecessary. But since you used the Center widget, I tought it would be interesting to know how to customize it.
Here is a View you wrap around your image widget
Includes a click event which opens up a full screen view of the image
Zoom and Pan image
Null-safety
Dark/Light background for PNGs
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ImageFullScreenWrapperWidget extends StatelessWidget {
final Image child;
final bool dark;
ImageFullScreenWrapperWidget({
required this.child,
this.dark = true,
});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
opaque: false,
barrierColor: dark ? Colors.black : Colors.white,
pageBuilder: (BuildContext context, _, __) {
return FullScreenPage(
child: child,
dark: dark,
);
},
),
);
},
child: child,
);
}
}
class FullScreenPage extends StatefulWidget {
FullScreenPage({
required this.child,
required this.dark,
});
final Image child;
final bool dark;
#override
_FullScreenPageState createState() => _FullScreenPageState();
}
class _FullScreenPageState extends State<FullScreenPage> {
#override
void initState() {
var brightness = widget.dark ? Brightness.light : Brightness.dark;
var color = widget.dark ? Colors.black12 : Colors.white70;
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top]);
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: color,
statusBarColor: color,
statusBarBrightness: brightness,
statusBarIconBrightness: brightness,
systemNavigationBarDividerColor: color,
systemNavigationBarIconBrightness: brightness,
));
super.initState();
}
#override
void dispose() {
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
// Restore your settings here...
));
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: widget.dark ? Colors.black : Colors.white,
body: Stack(
children: [
Stack(
children: [
AnimatedPositioned(
duration: Duration(milliseconds: 333),
curve: Curves.fastOutSlowIn,
top: 0,
bottom: 0,
left: 0,
right: 0,
child: InteractiveViewer(
panEnabled: true,
minScale: 0.5,
maxScale: 4,
child: widget.child,
),
),
],
),
SafeArea(
child: Align(
alignment: Alignment.topLeft,
child: MaterialButton(
padding: const EdgeInsets.all(15),
elevation: 0,
child: Icon(
Icons.arrow_back,
color: widget.dark ? Colors.white : Colors.black,
size: 25,
),
color: widget.dark ? Colors.black12 : Colors.white70,
highlightElevation: 0,
minWidth: double.minPositive,
height: double.minPositive,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
onPressed: () => Navigator.of(context).pop(),
),
),
),
],
),
);
}
}
Example Code:
ImageFullScreenWrapperWidget(
child: Image.file(file),
dark: true,
)
This is another option:
return new DecoratedBox(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('images/lake.jpg'),
fit: BoxFit.fill
),
),
);
For Image from asset
new Image(
image: AssetImage('images/test.jpg'),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
alignment: Alignment.center,
),
For some reason, the solutions given in the answers here did not work for me. The below code worked for me.
body: Container(
height: double.infinity,
width: double.infinity,
child: FittedBox(child: Image.asset('assets/thunderbackground.jpg'),
fit: BoxFit.cover),
you could try wrapping image.network in a a container with infinite dimensions which takes the available size of its parent (meaning if you drop this container in lower half of screen it will fill the lower half of screen if you put this directly as the body of scaffold it will take the full screen)
Container(
height: double.infinity,
width: double.infinity,
child: Image.network(
backgroundImage1,
fit: BoxFit.cover,
)
);
You can use MediaQuery class if you want to get the precious size of your device and use it to manage the size of your image, here's the examples:
return Container(
color: Colors.white,
child: Image.asset(
'assets/$index.jpg',
fit: BoxFit.fill,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
),
);
Here is an example of a FadeInImage with another widget overlay using the double.infinity method as in the accepted answer.
class FullScreenImage extends StatelessWidget {
#override
Widget build(BuildContext context) {
//you do not need container here, STACK will do just fine if you'd like to
//simplify it more
return Container(
child: Stack(children: <Widget>[
//in the stack, the background is first. using fit:BoxFit.cover will cover
//the parent container. Use double.infinity for height and width
FadeInImage(
placeholder: AssetImage("assets/images/blackdot.png"),
image: AssetImage("assets/images/woods_lr_50.jpg"),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
//if you use a larger image, you can set where in the image you like most
//width alignment.centerRight, bottomCenter, topRight, etc...
alignment: Alignment.center,
),
_HomepageWords(context),
]),
);
}
}
//example words and image to float over background
Widget _HomepageWords(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
child: Padding(
padding: EdgeInsets.all(30),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, 40, 0, 12),
child: Image.asset(
"assets/images/Logo.png",
height: 90,
semanticLabel: "Logo",
),
),
Text(
"ORGANIZATION",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
),
Text(
"DEPARTMENT",
style: TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
color: Colors.white),
),
Text(
"Disclaimer information...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.white),
),
],
),
),
onTap: () {
//to another screen / page or action
},
),
],
);
}
Use the below code if height: double.infinity, width: double.infinity, doesn't work to u.
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => new _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
Timer(Duration(seconds: 30),()=>Navigator.push(
context, MaterialPageRoute(builder: (context)=>Login())));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
//backgroundColor: Colors.white,
body: Container(
child: new Column(children: <Widget>[
new Image.asset(
'assets/image/splashScreen.png',
fit: BoxFit.fill,
// height: double.infinity,
// width: double.infinity,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
alignment: Alignment.center,
repeat: ImageRepeat.noRepeat,
),
]),
),
);
}
}
I'm trying to control the visibility of the smaller items of the FAB, according to the gif below:
However I am not able to insert the opacity in the items. Anywhere I put some kind of error occurs. I do not know if opacity is the best way to do it.
To hide the items I believe that with animation it is possible to control the time in which they will appear.
Below is what I have achieved so far:
Could you help me solve this problem?
Below is the above gif code:
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new HomePage()));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
int _angle = 90;
bool _isRotated = true;
void _rotate(){
setState((){
if(_isRotated) {
_angle = 45;
_isRotated = false;
} else {
_angle = 90;
_isRotated = true;
}
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new Positioned(
bottom: 200.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo1',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
new Material(
color: new Color(0xFF9E9E9E),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
],
),
)
),
new Positioned(
bottom: 144.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo2',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
new Material(
color: new Color(0xFF00BFA5),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
],
),
)
),
new Positioned(
bottom: 88.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo3',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
],
),
)
),
new Positioned(
bottom: 16.0,
right: 16.0,
child: new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 56.0,
height: 56.00,
child: new InkWell(
onTap: _rotate,
child: new Center(
child: new RotationTransition(
turns: new AlwaysStoppedAnimation(_angle / 360),
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
)
),
)
),
)
),
),
]
)
);
}
}
I used Opacity but it became redundant after using ScaleTransition. With ScaleTransition it is possible to hide and show widgets.
I used 3 animations to be able to generate the cascade effect.
Below is the code and its result:
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new HomePage()));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
int _angle = 90;
bool _isRotated = true;
AnimationController _controller;
Animation<double> _animation;
Animation<double> _animation2;
Animation<double> _animation3;
#override
void initState() {
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 180),
);
_animation = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.0, 1.0, curve: Curves.linear),
);
_animation2 = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.5, 1.0, curve: Curves.linear),
);
_animation3 = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.8, 1.0, curve: Curves.linear),
);
_controller.reverse();
super.initState();
}
void _rotate(){
setState((){
if(_isRotated) {
_angle = 45;
_isRotated = false;
_controller.forward();
} else {
_angle = 90;
_isRotated = true;
_controller.reverse();
}
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new Positioned(
bottom: 200.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new ScaleTransition(
scale: _animation3,
alignment: FractionalOffset.center,
child: new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo1',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
),
new ScaleTransition(
scale: _animation3,
alignment: FractionalOffset.center,
child: new Material(
color: new Color(0xFF9E9E9E),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){
if(_angle == 45.0){
print("foo1");
}
},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
),
],
),
)
),
new Positioned(
bottom: 144.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new ScaleTransition(
scale: _animation2,
alignment: FractionalOffset.center,
child: new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo2',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
),
new ScaleTransition(
scale: _animation2,
alignment: FractionalOffset.center,
child: new Material(
color: new Color(0xFF00BFA5),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){
if(_angle == 45.0){
print("foo2");
}
},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
),
],
),
)
),
new Positioned(
bottom: 88.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new ScaleTransition(
scale: _animation,
alignment: FractionalOffset.center,
child: new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo3',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
),
new ScaleTransition(
scale: _animation,
alignment: FractionalOffset.center,
child: new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){
if(_angle == 45.0){
print("foo3");
}
},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
),
],
),
)
),
new Positioned(
bottom: 16.0,
right: 16.0,
child: new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 56.0,
height: 56.00,
child: new InkWell(
onTap: _rotate,
child: new Center(
child: new RotationTransition(
turns: new AlwaysStoppedAnimation(_angle / 360),
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
)
),
)
),
)
),
),
]
)
);
}
}
you can see Flutter floating action button with speed dial
or
use https://pub.dartlang.org/packages/flutter_speed_dial
or
flutter Visibility widget
or
https://medium.com/#agungsurya/create-a-simple-animated-floatingactionbutton-in-flutter-2d24f37cfbcc