how to create a custom popup menu with flutter - dart

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,
),
)
],
),
),
),

Related

Flutter - Custom sliver app bar with search bar

I want to have a custom sliver appBar with a search bar in it. I made a normal app bar that looks like this : But I want that when we scroll down, the app bar looks like that :
Actually, the code of the normal app bar is just a green AppBar of elevation: 0 and just below I add my Header(). Here's the code of my Header :
class Header extends StatefulWidget {
String title;
IconData icon;
Header({#required this.title, #required this.icon});
#override
_HeaderState createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
TextEditingController _editingController;
#override
void initState() {
super.initState();
_editingController = TextEditingController();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return PreferredSize(
preferredSize: size,
child: Container(
margin: EdgeInsets.only(bottom: kDefaultPadding * 2.5),
height: size.height*0.2,
child: Stack(
children: [
Container(
height: size.height*0.2-27,
width: size.width,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(36),
bottomRight: Radius.circular(36),
)
),
child: Align(
alignment: Alignment.topCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(widget.title, style: Theme.of(context).textTheme.headline4.copyWith(color: Colors.white, fontWeight: FontWeight.bold)),
SizedBox(width: 20,),
Icon(widget.icon, size: 40, color: Colors.white,)
],
)),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
alignment: Alignment.center,
margin: EdgeInsets.symmetric(horizontal: kDefaultPadding),
padding: EdgeInsets.symmetric(horizontal: kDefaultPadding),
height: 54,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(
offset: Offset(0, 10),
blurRadius: 50,
color: Theme.of(context).primaryColor.withOpacity(0.23),
)]
),
child: Row(
children: [
Expanded(
child: TextField(
controller: _editingController,
textAlignVertical: TextAlignVertical.center,
onChanged: (_) => setState(() {}),
decoration: InputDecoration(
hintText: 'Search',
hintStyle: TextStyle(color: Theme.of(context).primaryColor.withOpacity(0.5)),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
),
),
_editingController.text.trim().isEmpty ? IconButton(
icon: Icon(Icons.search, color: Theme.of(context).primaryColor.withOpacity(0.5)),
onPressed: null) :
IconButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
icon: Icon(Icons.clear, color: Theme.of(context).primaryColor.withOpacity(0.5)),
onPressed: () => setState(() {
_editingController.clear();
})),
],
),
),
)
],
),
),
);
}
#override
void dispose() {
_editingController.dispose();
super.dispose();
}
}
Any help to build this is welcome.
I've made a simple example to show the main logic.
Create your own SliverPersistentHeaderDelegate and calculate shrinkFactor.
import 'dart:math';
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(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white70,
body: CustomScrollView(
slivers: [
SliverPersistentHeader(
pinned: true,
floating: false,
delegate: SearchHeader(
icon: Icons.terrain,
title: 'Trees',
search: _Search(),
),
),
SliverFillRemaining(
hasScrollBody: true,
child: ListView(
physics: NeverScrollableScrollPhysics(),
children: [
Text('some text'),
Placeholder(
color: Colors.red,
fallbackHeight: 200,
),
Container(
color: Colors.blueGrey,
height: 500,
)
],
),
)
],
),
);
}
}
class _Search extends StatefulWidget {
_Search({Key key}) : super(key: key);
#override
__SearchState createState() => __SearchState();
}
class __SearchState extends State<_Search> {
TextEditingController _editingController;
#override
void initState() {
super.initState();
_editingController = TextEditingController();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: TextField(
controller: _editingController,
// textAlignVertical: TextAlignVertical.center,
onChanged: (_) => setState(() {}),
decoration: InputDecoration(
hintText: 'Search',
hintStyle: TextStyle(
color: Theme.of(context).primaryColor.withOpacity(0.5)),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
),
),
_editingController.text.trim().isEmpty
? IconButton(
icon: Icon(Icons.search,
color: Theme.of(context).primaryColor.withOpacity(0.5)),
onPressed: null)
: IconButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
icon: Icon(Icons.clear,
color: Theme.of(context).primaryColor.withOpacity(0.5)),
onPressed: () => setState(
() {
_editingController.clear();
},
),
),
],
),
);
}
}
class SearchHeader extends SliverPersistentHeaderDelegate {
final double minTopBarHeight = 100;
final double maxTopBarHeight = 200;
final String title;
final IconData icon;
final Widget search;
SearchHeader({
#required this.title,
this.icon,
this.search,
});
#override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
var shrinkFactor = min(1, shrinkOffset / (maxExtent - minExtent));
var topBar = Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
alignment: Alignment.center,
height:
max(maxTopBarHeight * (1 - shrinkFactor * 1.45), minTopBarHeight),
width: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(title,
style: Theme.of(context).textTheme.headline4.copyWith(
color: Colors.white, fontWeight: FontWeight.bold)),
SizedBox(
width: 20,
),
Icon(
icon,
size: 40,
color: Colors.white,
)
],
),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(36),
bottomRight: Radius.circular(36),
)),
),
);
return Container(
height: max(maxExtent - shrinkOffset, minExtent),
child: Stack(
fit: StackFit.loose,
children: [
if (shrinkFactor <= 0.5) topBar,
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsets.only(
bottom: 10,
),
child: Container(
alignment: Alignment.center,
child: search,
width: 200,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.white,
boxShadow: [
BoxShadow(
offset: Offset(0, 10),
blurRadius: 10,
color: Colors.green.withOpacity(0.23),
)
]),
),
),
),
if (shrinkFactor > 0.5) topBar,
],
),
);
}
#override
double get maxExtent => 230;
#override
double get minExtent => 100;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}

Scroll controller is not attached to any scroll views

I am attaching scrollController to a listView builder. I am controlling scrolling using Slider.B ut I am getting error "Scroll controller is not attached to any scroll views".If I set max of Slider to some static value error is gone but if i set max = scrollController.position.maxScrollExtent it gives error.
scrollController = ScrollController(initialScrollOffset: 0.0);
scrollController.addListener(scrollListener);
Container(
decoration: BoxDecoration(color: Colors.black),
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
controller: scrollController,
scrollDirection: Axis.horizontal,
itemCount: totalSegments,
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: 70.0,
width: 148.0,
margin: EdgeInsets.only(top: 10.0),
decoration: BoxDecoration(
color: Colors.grey[800],
),
child: Row(
children: <Widget>[
Container(
width: 20.0,
decoration: BoxDecoration(color: Colors.grey[900]),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
width: 7.0,
height: 7.0,
decoration:
BoxDecoration(color: Colors.white),
),
Container(
width: 7.0,
height: 7.0,
decoration:
BoxDecoration(color: Colors.white),
),
Container(
width: 7.0,
height: 7.0,
decoration:
BoxDecoration(color: Colors.white),
),
],
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(5.0),
),
margin:
new EdgeInsets.only(top: 1.0, bottom: 1.0),
child: selectedSegment[index] != null
? SizedBox(
height: double.infinity,
width: double.infinity,
child: Center(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(
selectedSegment[index]
['segmentUrl']),
),
),
)),
)
: Container(),
),
),
],
),
),
Container(
width: 148.0,
height: MediaQuery.of(context).size.height - 130.0,
child: ListView(
scrollDirection: Axis.vertical,
children: buildSegmentList(index),
),
),
],
);
},
),
),
Try this code i got bottom and up event state in listview..
class ScrollViewDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: ScrollViewTest(),
);
}
}
class ScrollViewTest extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return ScrollViewState();
}
}
class ScrollViewState extends State<ScrollViewTest> {
ScrollController _controller;
String message = "";
_scrollListener() {
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
setState(() {
message = "reach the bottom";
});
}
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
setState(() {
message = "reach the top";
});
}
}
#override
void initState() {
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Scrollview Demo"),
),
body: Column(
children: <Widget>[
Container(
height: 50.0,
color: Colors.green,
child: Center(
child: Text(message),
),
),
Expanded(
child: ListView.builder(
controller: _controller,
itemCount: 30,
itemBuilder: (context, index) {
return ListTile(title: Text("Index : $index"));
},
),
),
],
),
);
}
}

How can I resize CircularProgressIndicator in flutter?

I saw this question and this issue, but it couldn't solve my problem. I tried SizedBox and SizedBox.fromSize and also BoxConstraints, but all of them didn't work.
How can I handle it?
import 'package:flutter/material.dart';
import 'package:mymovie/bloc_helpers/bloc_event_state_builder.dart';
import 'package:mymovie/logics/intro/intro.dart';
import 'package:mymovie/resources/strings.dart';
import 'package:mymovie/utils/bloc_navigator.dart';
import 'package:mymovie/utils/bloc_snackbar.dart';
import 'package:mymovie/utils/service_locator.dart';
class IntroScreen extends StatefulWidget{
#override
_IntroScreenState createState() => _IntroScreenState();
}
class _IntroScreenState extends State<IntroScreen> with SingleTickerProviderStateMixin{
final IntroBloc introBloc = sl.get<IntroBloc>();
AnimationController animationController;
Animation buttonSqueeze;
#override
void initState() {
super.initState();
animationController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this
);
buttonSqueeze = Tween(
begin: 320.0,
end: 70.0
).animate(CurvedAnimation(
parent: animationController,
curve: Interval(0.0,0.250)
));
animationController.addListener(() {
if(animationController.isCompleted) {
introBloc.emitEvent(IntroEventKakaoLogin());
}
});
}
Future<void> _playAnimation() async{
try {
await animationController.forward();
} on TickerCanceled {}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/ironman.jpg'),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.6), BlendMode.darken)
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.only(top: 180.0),
child: Text(
'영화일기',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 70.0
),
),
),
SizedBox(height: 20.0),
Container(
color: Colors.white,
width: 200.0,
height: 4.0,
),
SizedBox(height: 200.0),
AnimatedBuilder(
animation: buttonSqueeze,
builder: (_,__){
return BlocBuilder(
bloc: introBloc,
builder: (context, IntroState state){
if(state.isKakaoLoginSucceeded) {
BlocNavigator.pushReplacementNamed(context, routeHome);
}
if(state.isKakaoLoginFailed) {
BlocSnackbar.show(context, '로그인에 실패하였습니다.');
introBloc.emitEvent(IntroEventStateClear());
}
return GestureDetector(
child: Container(
width: buttonSqueeze.value,
height: 60.0,
decoration: BoxDecoration(
color: Colors.yellow,
borderRadius: BorderRadius.circular(15.0)
),
child: buttonSqueeze.value>75.0 ? Row(
children: <Widget>[
SizedBox(width: 20.0),
buttonSqueeze.value<100.0 ? Container() :
Image.asset('assets/images/kakao.png'),
buttonSqueeze.value<300.0 ? Container() :
Container(
child: Text(
'카카오톡으로 로그인',
style: TextStyle(
color: Colors.brown,
fontWeight: FontWeight.bold,
fontSize: 20.0
),
),
)
],
) :
SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.brown),
strokeWidth: 1.0,
),
width: 30.0,
height: 30.0,
)
),
onTap: () => _playAnimation()
);
}
);
}
)
],
)
)
);
}
}
Try this way, this solved my problem:
new Container(
height: 60.0,
child: new Center(child: new CircularProgressIndicator()),
)

How build a widget with meter with arrow like in photo provided in flutter

I want to create a widget that will build describe in this photo
I already created the meter bar but I still don't know how to add numbers from start to end of bar and a arrow in bottom where place what points you have and with same color above of it
child: Row(children: <Widget>[
Column(children: <Widget>[
Padding(
padding: EdgeInsets.all(5.0),
child: Container(
decoration: new BoxDecoration(
border: new Border(right: BorderSide(color: Colors.black))
),
child: Column(
children: <Widget>[
Text('Points'),
Text('38'),
],
),
),
),
],),
// green bar
Column(children: <Widget>[
Padding(
padding: EdgeInsets.only(right:10.0),
child: Container(
width:ratewidth,
decoration: new BoxDecoration(
border: new Border(bottom: BorderSide(color: Colors.green, width: 5.0))
),
),
)
],),
//yellow bar
Column(children: <Widget>[
Padding(
padding: EdgeInsets.only(right:10.0),
child: Container(
width:ratewidth,
decoration: new BoxDecoration(
border: new Border(bottom: BorderSide(color: Colors.yellow, width: 5.0))
),
),
),
],),
...
],)
With a combination of Row, Column and Align it can be done in a few lines.
The hardest part is actually the triangle. Usually, you'll want to use CustomPainter for the triangle, but I was lazy here. So I combined translation, rotation, and a clip.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHome(),
);
}
}
class MyHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: ScoreMeter(
score: 1,
),
)
],
),
);
}
}
class ScoreMeter extends StatelessWidget {
final int score;
ScoreMeter(
{
this.score,
Key key})
: super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
height: 100.0,
child: Row(
children: <Widget>[
Expanded(
child: ScoreMeterItem(
score: score, color: Colors.green, minRange: 0, maxRange: 50),
),
Expanded(
child: ScoreMeterItem(
score: score,
color: Colors.yellow,
minRange: 51,
maxRange: 100),
),
Expanded(
child: ScoreMeterItem(
score: score,
color: Colors.orange,
minRange: 101,
maxRange: 150),
),
Expanded(
child: ScoreMeterItem(
score: score, color: Colors.red, minRange: 151, maxRange: 200),
),
Expanded(
child: ScoreMeterItem(
score: score,
color: Colors.purple,
minRange: 201,
maxRange: 250),
),
Expanded(
child: ScoreMeterItem(
score: score,
color: Colors.brown,
minRange: 251,
maxRange: 300),
),
],
),
);
}
}
class ScoreMeterItem extends StatelessWidget {
/// Hello World
final int score;
final Color color;
final int minRange;
final int maxRange;
ScoreMeterItem(
{this.score,
this.color = Colors.grey,
#required this.minRange,
#required this.maxRange,
Key key})
: assert(minRange != null),
assert(maxRange != null),
super(key: key);
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(minRange.toString(), style: theme.textTheme.caption),
Text(maxRange.toString(), style: theme.textTheme.caption),
],
),
ScoreMeterBar(color: color),
score >= minRange && score <= maxRange
? SizedBox(
height: 10.0,
child: Align(
alignment: Alignment(
(score - minRange) / (maxRange - minRange) * 2 - 1,
0.0),
child: Arrow(color: color),
),
)
: SizedBox()
],
),
);
}
}
class Arrow extends StatelessWidget {
final Color color;
Arrow({this.color});
#override
Widget build(BuildContext context) {
return SizedBox(
height: 5.0,
width: 10.0,
child: ClipRect(
child: OverflowBox(
maxWidth: 10.0,
maxHeight: 10.0,
child: Align(
alignment: Alignment.topCenter,
child: Transform.translate(
offset: Offset(.0, 5.0),
child: Transform.rotate(
angle: pi / 4,
child: Container(
width: 10.0,
height: 10.0,
color: color,
),
),
),
),
),
),
);
}
}
class ScoreMeterBar extends StatelessWidget {
final Color color;
ScoreMeterBar({this.color = Colors.grey, Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: 8.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(4.0),
),
color: color,
),
);
}
}

How to show fullscreen image in flutter

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,
),
]),
),
);
}
}

Resources