Fading Edge ListView - Flutter - dart

has anyone come across something like fadingEdgeLength in Android for Flutter so that when you scroll up items start fading into the top of the screen?
Below is my interface built up of the Widgets.
If it helps these are the properties I'm referring to:
android:fadingEdgeLength="10dp"
android:requiresFadingEdge="horizontal">
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CMS Users'),
),
body: ListView.builder(
padding: EdgeInsets.only(top: 20.0, left: 4.0),
itemExtent: 70.0,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10.0,
child: InkWell(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new PeopleDetails("Profile Page", profiles[index]),
));
},
child: ListTile(
leading: CircleAvatar(
child: Text(profiles[index].getInitials()),
backgroundColor: Colors.deepPurple,
radius: 30.0,
),
title: Text(
data[index]["firstname"] + "." + data[index]["lastname"]),
subtitle: Text(
data[index]["email"] + "\n" + data[index]["phonenumber"]),
),
),
);
}),
);
}
}

As others have mentioned, you can put the ListView under a ShaderMask, but with minor extra parameterizations you can get much better results - at least if you want to achieve what I wanted.
Optionally you can set the [stops] list for the LinearGradient:
The [stops] list, if specified, must have the same length as [colors]. It specifies fractions of the vector from start to end, between 0.0 and 1.0, for each color.
Plus: There are blend modes, where the color channels of the source are ignored, only the opacity has an effect. BlendMode.dstOut is also such in the example below. As you can see in the screenshot, the purple color is not used concretely, only for the fractions of the vector.
You can play with the different [blendMode] settings, there are quite a few of them.
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: FadingListViewWidget(),
),
),
);
}
class FadingListViewWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 320,
child: ShaderMask(
shaderCallback: (Rect rect) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.purple, Colors.transparent, Colors.transparent, Colors.purple],
stops: [0.0, 0.1, 0.9, 1.0], // 10% purple, 80% transparent, 10% purple
).createShader(rect);
},
blendMode: BlendMode.dstOut,
child: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Card(
color: Colors.orangeAccent,
child: ListTile(
title: Text('test test test test test test'),
),
);
},
),
),
),
);
}
}

You could apply a ShaderMask on top of ListView and use BlendMode to get what you want.
Widget animationsList() {
return Expanded(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Colors.transparent,Colors.red],
).createShader(bounds);
},
child: Container(height: 200.0, width: 200.0, color: Colors.blue,),
blendMode: BlendMode.dstATop,
),
);

I had similar request so I created a library for this task.
You can find it here: fading_edge_scrollview
To use it you need to add a ScrollController to your ListView and then pass this ListView as child to FadingEdgeScrollView.fromScrollView constructor

Wrap the Listview with Stack, add the Listview as the first child, the second is Positioned Container with LinearGradient.
Sample from my code:
Stack:
return Stack(
children: <Widget>[
ListView(
scrollDirection: Axis.horizontal,
children: _myListOrderByDate,
),
FadeEndListview(),
],
);
The overlay class:
class FadeEndListview extends StatelessWidget {
const FadeEndListview({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Positioned(
right: 0,
width: 8.0,
height: kYoutubeThumbnailsHeight,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerRight,
end: Alignment.centerLeft,
stops: [0.0, 1.0],
colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).scaffoldBackgroundColor.withOpacity(0.0),
],
),
),
),
);
}
}
And it will look something like this:

Try to use
Text(
'This is big text, I am using Flutter and trying to fade text',
overflow: TextOverflow.fade,
maxLines: 1,
),

Related

How to have four of the same custom widget have text change individually?

I am looking to create a grid with 4 custom widgets that can either add or subtract from a given starting number. See image for reference.
For example, if you press player one, the number would increase or decrease to 100 or 99. But the other 3 players would remain the same.
I had originally used one stateful widget with a separate function for each player, but I am sure there's a way to do it in a more modular way.
class CommanderDamage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return CommanderDamageState();
}
}
class CommanderDamageState extends State<CommanderDamage> {
int damage = 0;
void update() {
setState(() {
damage++;
});
}
#override
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: 4,
itemBuilder: (BuildContext context, index) {
return Container(
child: Column(
children: <Widget>[
Text("Player " + index.toString()),
InkWell(
onTap: update,
child: Container(
width: 100.0,
height: 100.0,
child: Text(damage),
)
],
),
);
},
),
),
);
}
}
EDIT: I have edited my code to reflect my current. Currently, when the damage area is pressed, the damage increases for all 4 players instead of the one I am pressing.
Wrap your text widget inside InkWell(). Basically what InkWell does is creates a rectangular touch responsive area.
InkWell(
child: Text(
'Player One',
style: TextStyle(
fontSize: 20, color: Colors.white),
onTap: () {
// Your function
}
)
But this make the interactive tap area according to size of the text which is very small, so it's better to wrap it inside a container and provide height-width or some space with padding
InkWell(
child: Container(
width: 100,
height: 100,
child: Text(
'Player One',
style: TextStyle(
fontSize: 20, color: Colors.white), ),
onTap: () {
// Your function
}
)
An inside onTap you can your function and perform changes.
Read more about InkWell:
https://docs.flutter.io/flutter/material/InkWell-class.html
After lots of trial and error I managed to find an answer.
I had to set the state within the onTap instead of making a separate function and calling it in the onTap.
class CommanderDamage extends StatefulWidget {
int damage = 0;
CommanderDamage({this.damage, Key key});
#override
State<StatefulWidget> createState() {
return CommanderDamageState();
}
}
class CommanderDamageState extends State<CommanderDamage> {
var damage = [0, 0, 0, 0, 0, 0];
#override
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft, end: Alignment.bottomRight,
colors: [Color(0xfff6921e), Color(0xffee4036)],
),
),
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: damage.length,
itemBuilder: (BuildContext context, index) {
return Container(
child: Column(
children: <Widget>[
InkWell(
onTap: () {
setState(() {
damage[index]++;
});
},
onLongPress: () {
setState(() {
damage[index] = 0;
});
},
child: Container(
width: 100.0,
height: 100.0,
child: Text(damage[index].toString()),
),
),
],
),
);
},
),
),
],
),
),
);
}
}

Flutter bottom sheet corner radius

I am writing an app that needs to have a bottomsheet with corner radius. Something like you can see in the Google Task app.
Here is the code I have
showModalBottomSheet(
context: context,
builder: (builder) {
return new Container(
height: 350.0,
color: Colors.transparent,
child: new Container(
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0), topRight: const Radius.circular(10.0))),
child: new Center(
child: new Text("This is a modal sheet"),
)),
);
});
Is still shows the sheet without the border radius.
Okay, I found a reason. It is indeed displaying the rounded corner but the background of the Container is staying white due to Scaffold background color.
Now the question is how do I override the Scaffold background color.
For those who still trying to resolve this:
for some reasons Colors.transparent does not work, so all you need to do is change color to : Color(0xFF737373)
showModalBottomSheet(
context: context,
builder: (builder) {
return new Container(
height: 350.0,
color: Color(0xFF737373),
child: new Container(
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0), topRight: const Radius.circular(10.0))),
child: new Center(
child: new Text("This is a modal sheet"),
)),
);
});
use shape property inside show showModalBottomSheet and give it RoundedRectangleBorder.
showModalBottomSheet(
context: context,
shape : RoundedRectangleBorder(
borderRadius : BorderRadius.circular(20)
),
builder: (builder) {
return new Container(
height: 350.0,
color: Color(0xFF737373),
child: new Container(
child: new Center(
child: new Text("This is a modal sheet"),
)),
);
});
_settingModalBottomSheet(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc){
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(ScreenUtil().setWidth(16)),
topRight: Radius.circular(ScreenUtil().setWidth(16))
),
),
);
}
);
}
It looks like this:
result_1
After add below code in main.dart:
return MaterialApp(
theme: ThemeData(
canvasColor: Colors.transparent
),
);
It looks like this:
result_2
Use this code, it's working perfectly for me!
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0))
Here is the full code.
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
context: context,
builder: (BuildContext bc) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter state) {
return SingleChildScrollView(
child: InkWell(
onTap: () {},
child: Container(
margin:
EdgeInsets.only(left: 10, right: 10, bottom: 10),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: Container()))));
});
});
Output:
Okay, so changing the canvasColor in my app's main theme to Colors.transparent worked.
USE ClipRRect Widget like below.
showMaterialModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context, scrollController) =>
ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Container(
height:
MediaQuery.of(context).size.height *
0.95,
child: whateverChild();
Use shape property.
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(40),
topLeft: Radius.circular(40),
),
),
builder: (builder) {
return new Container(
height: 350.0,
color: Colors.transparent,
child: YourWidget(),
});
You should also check the widgets on the sheet.
Even if the border is applied, it seems that other widgets fill the space and are not applied.
Use padding, etc. to secure the upper space of the seat.
main_screen file:
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Column(
children: [
InkWell(
onTap: () {
_scaffoldKey.currentState!
.showBottomSheet(
(context) => const AddItemBottomSheet(),
backgroundColor: Colors.transparent,
);
},
child: Column(children: [
const Icon(
Icons.add_circle_outline_rounded,
size: 32,
),
Text(
'Add Item',
),
]),
),
]
)
);
}
add_item_bottom_sheet.dart
class AddItemBottomSheet extends StatefulWidget {
const AddItemBottomSheet({Key? key}) : super(key: key);
#override
State<AddItemBottomSheet> createState() => _AddItemBottomSheetState();
}
class _AddItemBottomSheetState extends State<AddItemBottomSheet> {
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 60.0,
color: Colors.black.withOpacity(0.2),
spreadRadius: 50,
offset: const Offset(0.0, -10.0)),
],
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
//Your UI items on BottomSheet
],
),
),
),
);
}
}
I had exactly the same issue when I was using the modal_bottom_sheet library. but after I set the backgroundColor to transparent... it worked like magic
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent, // Add this line of Code
builder: (builder) {
return new Container(
height: 350.0,
color: Colors.transparent,
child: new Container(
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0), topRight: const Radius.circular(10.0))),
child: new Center(
child: new Text("This is a modal sheet"),
)),
);
});

Scrollable flutter popup menu

I'm trying to use flutter popup menu button, but I can't seem to make it smaller with a scroll.
Is it doable? Or am I using the wrong widget to do it?
Image below as reference, would like to show only the first 4 / 5 items, and scroll to show the rest!
Thanks in advance!
You can create your own PopUp Widget instead.
A Card wrapped into a AnimatedContainer with specific dimensions and a ListView inside.
Place this widget on your screen using Stack and Positioned widgets so it will be above other elements on the top | right.
class CustomPopup extends StatefulWidget {
CustomPopup({
#required this.show,
#required this.items,
#required this.builderFunction,
});
final bool show;
final List<dynamic> items;
final Function(BuildContext context, dynamic item) builderFunction;
#override
_CustomPopupState createState() => _CustomPopupState();
}
class _CustomPopupState extends State<CustomPopup> {
#override
Widget build(BuildContext context) {
return Offstage(
offstage: !widget.show,
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: widget.show ? MediaQuery.of(context).size.height / 3 : 0,
width: MediaQuery.of(context).size.width / 3,
child: Card(
elevation: 3,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: widget.items.length,
itemBuilder: (context, index) {
Widget item = widget.builderFunction(
context,
widget.items[index],
);
return item;
},
),
),
),
),
);
}
}
return Stack(
children: <Widget>[
Container(
color: Colors.blueAccent,
),
Positioned(
right: 0,
top: 60,
child: CustomPopup(
show: shouldShow,
items: [1, 2, 3, 4, 5, 6, 7, 8],
builderFunction: (context, item) {
return ListTile(
title: Text(item.toString()),
onTap: () {}
);
},
),
),
],
);
You can create this in two ways: the first one is PopupMenuButton widget and the second one is PopupRoute.
class HomePage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State {
Listitems = [1,2,3,4,5,6,7,8,9,10,11,12,13];
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(
child: PopupMenuButton(
child: Icon(Icons.add_shopping_cart),
offset: Offset(-1.0, -220.0),
elevation: 0,
color: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
itemBuilder: (context) {
return <PopupMenuEntry<Widget>>[
PopupMenuItem<Widget>(
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10))),
child: Scrollbar(
child: ListView.builder(
padding: EdgeInsets.only(top: 20),
itemCount: items.length,
itemBuilder: (context, index) {
final trans = items[index];
return ListTile(
title: Text(
trans.toString(),
style: TextStyle(
fontSize: 16,
),
),
onTap: () {
//what would you like to do?
},
);
},
),
),
height: 250,
width: 500,
),
)
];
}),
)
You can also adjust the number of items you want to show by reducing or increasing height of the container. I also added a scrollbar just in case.
You can use maxHeight for constrains property.
...
PopupMenuButton(
constraints:
BoxConstraints(minWidth: context.maxWidth, maxHeight: 300),
...

Horizontally scrollable cards with Snap effect in flutter

I want to create a list of cards scrolling horizontally with snap to fit effect when swiped either from left or right.
Each card has some spacing between them and fit to screen similar to below image
Apart from that these horizontally scrollable list elements should be contained inside a vertically scrollable list.
I all I am able to achieve is only displaying a list of horizontal scrolling cards after following example in flutter docs.
class SnapCarousel extends StatelessWidget {
#override
Widget build(BuildContext context) {
final title = 'Horizontal List';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
height: 200.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
width: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
color: Colors.green,
),
Container(
width: 160.0,
color: Colors.yellow,
),
Container(
width: 160.0,
color: Colors.orange,
),
],
),
),
),
);
}
}
Use PageView and ListView:
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: MyHomePage()));
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Carousel in vertical scrollable'),
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 16.0),
itemBuilder: (BuildContext context, int index) {
if(index % 2 == 0) {
return _buildCarousel(context, index ~/ 2);
}
else {
return Divider();
}
},
),
);
}
Widget _buildCarousel(BuildContext context, int carouselIndex) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Carousel $carouselIndex'),
SizedBox(
// you may want to use an aspect ratio here for tablet support
height: 200.0,
child: PageView.builder(
// store this controller in a State to save the carousel scroll position
controller: PageController(viewportFraction: 0.8),
itemBuilder: (BuildContext context, int itemIndex) {
return _buildCarouselItem(context, carouselIndex, itemIndex);
},
),
)
],
);
}
Widget _buildCarouselItem(BuildContext context, int carouselIndex, int itemIndex) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0),
child: Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
),
);
}
}
Screenshot:
If you don't want to use any 3rd party packages, you can simply try this:
class _HomePageState extends State<HomePage> {
int _index = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: SizedBox(
height: 200, // card height
child: PageView.builder(
itemCount: 10,
controller: PageController(viewportFraction: 0.7),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Center(
child: Text(
"Card ${i + 1}",
style: TextStyle(fontSize: 32),
),
),
),
);
},
),
),
),
);
}
}
this is an old question, and I arrived here looking for something else ;-), but what WitVault was lookig is done easy with this package: https://pub.dev/packages/flutter_swiper
The implementation:
Put the dependencies in pubsec.yaml:
dependencies:
flutter_swiper: ^1.1.6
Import it in the page where you need it:
import 'package:flutter_swiper/flutter_swiper.dart';
In the layout:
new Swiper(
itemBuilder: (BuildContext context, int index) {
return new Image.network(
"http://via.placeholder.com/288x188",
fit: BoxFit.fill,
);
},
itemCount: 10,
viewportFraction: 0.8,
scale: 0.9,
)
To achieve the snap effect via ListView, just set the physics to PageScrollPhysics
const List<Widget> children = [
ContainerCard(),
ContainerCard(),
ContainerCard(),
];
ListView.builder(
scrollDirection: Axis.horizontal,
physics: const PageScrollPhysics(), // this for snapping
itemCount: children.length,
itemBuilder: (_, index) => children[index],
)
Advanced Snap List
If you are looking for advanced usages, such as dynamic item sizes, configurable snap points, visualization of items, and essential control (such as scrollToIndex, animate) you should use the native-based SnappyListView with way more features.
SnappyListView(
itemCount: Colors.accents.length,
itemBuilder: (context, index) {
return Container(
height: 100,
color: Colors.accents.elementAt(index),
child: Text("Index: $index"),
),
);
I believe the answer solution from CopsOnRoad is better and simple for someone who don't want to use a 3rd party library. However, since there is no animation, I add the scale animation when the card is viewed (expand) and the previous card is swiped (shrink) using index. So what happened is whenever the first time the page load, 1st and 2nd card won't have any animation, and when the card is swiped, only the previous and current card have the scale animation. So this is my implementation:
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex = -1, previousIndex = 0;
double getAnimationValue(int currentIndex, int widgetIndex, int previousIndex,
{bool begin = true}) {
if (widgetIndex == currentIndex) {
return begin ? 0.9 : 1;
} else {
return begin ? 1 : 0.9;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 200, // card height
child: PageView.builder(
itemCount: 10,
controller: PageController(viewportFraction: 0.7),
onPageChanged: (int index) {
setState(() {
if (currentIndex != -1) {
previousIndex = currentIndex;
}
currentIndex = index;
});
},
itemBuilder: (_, widgetIndex) {
return (currentIndex != -1 &&
(previousIndex == widgetIndex ||
widgetIndex == currentIndex))
? TweenAnimationBuilder(
duration: const Duration(milliseconds: 400),
tween: Tween<double>(
begin: getAnimationValue(
currentIndex,
widgetIndex,
previousIndex,
),
end: getAnimationValue(
currentIndex,
widgetIndex,
previousIndex,
begin: false,
),
),
builder: (context, value, child) {
return Transform.scale(
scale: value,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Card${widgetIndex + 1}",
style: const TextStyle(fontSize: 30),
),
Text(
"$widgetIndex >> Widget Index << $widgetIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$currentIndex >> Current Index << $currentIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$previousIndex >> Previous Index << $previousIndex",
style: const TextStyle(fontSize: 22),
),
],
),
),
);
},
)
: Transform.scale(
// this is used when you want to disable animation when initialized the page
scale:
(widgetIndex == 0 && currentIndex == -1) ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Card${widgetIndex + 1}",
style: const TextStyle(fontSize: 30),
),
Text(
"$widgetIndex >> Widget Index << $widgetIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$currentIndex >> Init Index << $currentIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$previousIndex >> Previous Index << $previousIndex",
style: const TextStyle(fontSize: 22),
),
],
),
),
);
},
),
),
],
),
);
}
}
I used TweenAnimationBuilder for this animation and hardcoded the widget. You can use method for your widget or use package flutter_animate for easy animation whenever necessary.

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