How do I convert a listView to a GridView flutter? - user-interface

I have a scrollable list view in a horizontal format that I want to covert into a grid view with the same elements.
Container(
height: MediaQuery.of(context).size.height / 4,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: homeList.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (i == 0) {
_interstitialAd.show();
} else if (i == 1) {
} else if (i == 2) {
sendInvite();
}
},
);
},
),
)
This is what the list view looks like:
And this is what I want it to look like:

Flutter has the equivalent to ListView.builder for GridView.
You only need to tell it the number of columns/rows(depending in the orientation) you want.
GridView.builder(
itemCount: homeList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
if (i == 0) {
_interstitialAd.show();
} else if (i == 1) {
} else if (i == 2) {
sendInvite();
});
},
)

Related

Flutter ListView.separated frame drop when loading huge list

I'm trying to display a lazy list in flutter for windows. The list has approximately 2300 elements in it. The list is within a FutureBuilder, whose future is to fetch the 2300 elements from Hive database. Each element in the list is a MaterialButton with some properties. Im not getting smooth scrolling when scrolled fast. Some frames are being dropped. I have tried cacheextend and setting automatickeepalives to true. Still having the same problem. When ItemExtend is set to a large number(say 40), the scrollView works fine without frame drop. In release mode, it has better performance, but still some frames are being dropped. What is the best solution available to this problem?
//this Rawscrollbar is returned if the future is have some data
RawScrollbar(
isAlwaysShown: true,
controller: scrollControllerMLC,
thickness: context.percentWidth * .8,
radius: Radius.zero,
thumbColor:
SearchLeftContainerColors.headPoolListThumbColor,
child: ListView.separated(
padding: EdgeInsets.fromLTRB(
context.percentWidth * .5,
context.percentHeight * 0,
context.percentWidth * 1,
context.percentHeight * 1),
itemCount: lengthOfBoxes,
controller: scrollControllerMLC,
// addAutomaticKeepAlives: true,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, int index) {
return ListButtonMEDLC(data[index], index);
},
separatorBuilder: (BuildContext context, int index) {
return Divider(
color: SearchLeftContainerColors
.headsPoolSeparatorColour,
height: 1,
);
},
));
class ListButtonMEDLC extends StatelessWidget {
final String text;
final int index;
ListButtonMEDLC(this.text, this.index);
#override
Widget build(BuildContext context) {
return MaterialButton(
color:
(context.watch<ListButtonColorChangerMEDLC>().isSelectedList[index])
? SearchLeftContainerColors.headPoolListSelectedColor
: SearchLeftContainerColors.headPoolListColor,
hoverColor:
(context.watch<ListButtonColorChangerMEDLC>().isSelectedList[index])
? SearchLeftContainerColors.headPoolListSelectedColor
: SearchLeftContainerColors.headPoolListHoverColor,
highlightColor: SearchLeftContainerColors.headPoolListHighLightedColor,
child: Align(
alignment: Alignment.centerLeft,
child: Text(
text,
style: TextStyle(
fontSize: context.percentWidth * 1.1,
color: (context
.watch<ListButtonColorChangerMEDLC>()
.isSelectedList[index])
? Colors.white
: Colors.black),
),
),
onPressed: () {
context.read<ListButtonColorChangerMEDLC>().changeIsSelectedList(index);
},
);
}
}
//this it the future of the future builder;
loadDrugBox() async {
Map boxes = await DB.boxes.getBoxAsMap("drugs");
lengthOfBoxes = boxes.length;
return boxes;
}
//Provider
class ListButtonColorChangerMEDLC extends ChangeNotifier {
List<bool> isSelectedList = List.generate(lengthOfBoxes, (index) => false);
changeIsSelectedList(int indexOfSelected) {
for (int i = 0; i < lengthOfBoxes; i++) {
if (i == indexOfSelected) {
isSelectedList[i] = true;
} else
isSelectedList[i] = false;
}
notifyListeners();
}
}
Yes. I solved this issue by replacing material button which was an expensive widget with gestureDetector.

How to get original image size in Dart Image.network?

The following code gives Division by null error,
#override
Widget build(BuildContext context) {
Image image = Image.network(data['image-url']);
double widthToHeight = image.width / image.height;
if(widthToHeight <= 0.2) {
return NewsTileSmall(data: data);
} else {
return NewsTileLarge(data: data);
}
}
Best if, I could use a function to set the value of double widthToHeight.
In your build method you need to preload image.
Image image = new Image.network('image-url');
Completer<ui.Image> completer = new Completer<ui.Image>();
image.image
.resolve(new ImageConfiguration())
.addListener(ImageStreamListener(ImageInfo info, bool _) {
completer.complete(info.image));
})
Then you need to use FutureBuilder
FutureBuilder<ui.Image>(
future: completer.future,
builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
if (snapshot.hasData){
return your image Widget}
else{
return placeholder widget
}
completed build method
import 'dart:ui' as ui;
Image image = Image.network('image url');
final completer = Completer<ui.Image>();
image.image
.resolve(const ImageConfiguration()).
addListener(ImageStreamListener((ImageInfo info, bool syncCall) => completer.complete(info.image)));
return Scaffold(
appBar: AppBar(
title: const Text("Image Dimensions Example"),
),
body: FutureBuilder<ui.Image>(
future: completer.future,
builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
if (snapshot.hasData) {
return Text(
'${snapshot.data.width}x${snapshot.data.height}',
);
} else {
return const Text('Loading...');
}
},
)
);
});
To get the original size of a network image, you could use the image_pixels library.
Here is how to use it:
Widget buildContent(BuildContext context, String url) {
final width = MediaQuery.of(context).size.width; // This is the screen size width
final height = MediaQuery.of(context).size.height; // This is the screen size height
Image nimg = Image.network(url); // Preload the network image
return Stack(children: [
Center(
child: Image(
image: nimg.image,
fit: BoxFit.fill,
width: width,
height: height,
)
),
// Wrap the widget that need to access the original image size in ImagePixels
ImagePixels(
imageProvider: nimg.image,
builder: (context, img) {
return Positioned(
// Place a widget on top of the image, it's position related to the original image size
top: 92.0 * (height/img.height),
left: 137.0 * (width/img.width),
child: InkWell(
onTap: () {
print("On tap");
},
child:
Container(
width: 50,
height: 50,
color: Colors.red,
),
)
);
},
)
]);
}

How to create a Slide-In and Slide-Out animation in Flutter?

I am trying to create a Slide in and Slide out animation in Flutter. Animation should look like this:
----- Widget slides in ---> Wait for 1 seconds -----Widget slides out of screen -->
I have tried following code but my animation is stuck in a loop.
class _MyStatefulWidgetState extends State<MyStatefulWidget> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _positionAnimation;
Animation<double> opacityAnimation;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_positionAnimation = Tween<Offset>(
begin: const Offset(-1, 0),
end: const Offset(0, 0.0),
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.elasticOut),
)..addStatusListener((status) {
print('animation 1 status $status');
if (status == AnimationStatus.completed) {
_controller.reset();
}
if (status == AnimationStatus.dismissed) {
_positionAnimation = Tween<Offset>(
begin: const Offset(0, 0),
end: const Offset(1, 0.0),
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.elasticIn),
)..addStatusListener((status2) {
print('animation 2 status $status2');
if (status == AnimationStatus.dismissed) {
_controller.reset();
}
});
_controller.forward();
}
});
_controller.forward();
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
builder: _buildAnimation,
animation: _controller,
);
}
Widget _buildAnimation(BuildContext context, Widget child) {
return Opacity(
opacity: 1,
child: SlideTransition(
position: _positionAnimation,
child: Container(
color: Colors.blueAccent,
height: 100,
child: Center(
child: Text(
'Hello, World!',
style: Theme.of(context).textTheme.display1,
),
),
),
),
);
}
With this approach animation is stuck in a loop.
I cannot use staggered animation as I am trying to animate same property. (Or is there a way to use staggered animation on same property?).
Any better way for implementing this?
Taking idea from #pskink code in comments, achieved desired effect with following code using TweenAnimationBuilder:
class _SlideInOutWidgetState extends State<SlideInOutWidget>
with SingleTickerProviderStateMixin {
double startPos = -1.0;
double endPos = 0.0;
Curve curve = Curves.elasticOut;
#override
Widget build(BuildContext context) {
return TweenAnimationBuilder(
tween: Tween<Offset>(begin: Offset(startPos, 0), end: Offset(endPos, 0)),
duration: Duration(seconds: 1),
curve: curve,
builder: (context, offset, child) {
return FractionalTranslation(
translation: offset,
child: Container(
width: double.infinity,
child: Center(
child: child,
),
),
);
},
child: Text('animated text', textScaleFactor: 3.0,),
onEnd: () {
print('onEnd');
Future.delayed(
Duration(milliseconds: 500),
() {
curve = curve == Curves.elasticOut
? Curves.elasticIn
: Curves.elasticOut;
if (startPos == -1) {
setState(() {
startPos = 0.0;
endPos = 1.0;
});
}
},
);
},
);
}
}
Use Marquee plugin for text Animations
Install :
marquee: ^1.3.1
Example :
Marquee(
text: 'There once was a boy who told this story about a boy: "',
)
Fore more info try marquee | flutterpckage

Flutter: Overput image over image save it on image and then share it

I want to add a simple image over an image from the gallery/camera. The behavior should be like it.
Open the application, choose a photo or make one with the floating buttons, then show the image and over put our logo on the bottom left. All it is working now.
Then I would like to save it both images in a single one, but I don't know how this process is called or how to achieve it.
import 'package:share/share.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image Picker Demo',
home: MyHomePage(title: 'Image Picker Example'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File _imageFile;
dynamic _pickImageError;
String _retrieveDataError;
void _onImageButtonPressed(ImageSource source) async {
try {
_imageFile = await ImagePicker.pickImage(source: source);
} catch (e) {
_pickImageError = e;
}
setState(() {});
}
Widget _previewImage() {
final Text retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_imageFile != null) {
print('La imagen ha sido puesta en la pantalla?');
return Image.file(_imageFile);
} else if (_pickImageError != null) {
return Text(
'Pick image error: $_pickImageError',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
Future<void> retrieveLostData() async {
final LostDataResponse response = await ImagePicker.retrieveLostData();
if (response.isEmpty) {
return;
}
if (response.file != null) {
setState(() {
_imageFile = response.file;
});
} else {
_retrieveDataError = response.exception.code;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Stack(children: <Widget>[
Container(
color: Colors.amber,
child: Platform.isAndroid
? FutureBuilder<void>(
future: retrieveLostData(),
builder:
(BuildContext context, AsyncSnapshot<void> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
case ConnectionState.done:
return _previewImage();
default:
if (snapshot.hasError) {
return Text(
'Pick image/video error: ${snapshot.error}}',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
},
)
: (_previewImage())), //
Positioned(
bottom: 16,
left: 16,
width: 100,
height: 100,
child: Image.network(
'http://father-home.ru/wp-content/uploads/2018/05/cropped-logo8.png'))
]))),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () {
_onImageButtonPressed(ImageSource.gallery);
},
heroTag: 'image0',
tooltip: 'Pick Image from gallery',
child: const Icon(Icons.photo_library),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
_onImageButtonPressed(ImageSource.camera);
},
heroTag: 'image1',
tooltip: 'Take a Photo',
child: const Icon(Icons.camera_alt),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
_onShare();
},
tooltip: 'Share',
child: const Icon(Icons.share),
),
),
],
),
);
}
void _onShare(){
//TODO: save the two images into one and share
}
Text _getRetrieveErrorWidget() {
if (_retrieveDataError != null) {
final Text result = Text(_retrieveDataError);
_retrieveDataError = null;
return result;
}
return null;
}
}
the widget with this code looks like it:
You can use RepaintBoundary widget to export a specific widget to image.
I believe that you will need to work with Bitmap.
Load 2 bitmaps, one is the background, one is your logo.
Apply transformation as you like.
Save output bitmap to file.
Take a look at: https://pub.dev/packages/bitmap

How to scroll or jump to position of PageView.builder or PageController in Flutter?

Issue: Unable to scroll to POSITION after loading the Pageviews using PageController *
like ViewPager scroll to specific page in Android
Widget _buildCarousel(BuildContext context, int selectedIndex) {
PageController controller = PageController(viewportFraction: 1, keepPage: true);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
// you may want to use an aspect ratio here for tablet support
height: 400.0,
width: 240,
child: PageView.builder(
itemCount: assetImageList.length,
controller: controller,
itemBuilder: (BuildContext context, int itemIndex) {
return _buildCarouselItem(context, selectedIndex, itemIndex);
},
),
)
],
);
}
Finally found the answer. Just set the initialPage: mSelectedPosition attribute like:
child: PageView.builder(
itemCount: mTemplateModelList.length,
controller: PageController(initialPage: mSelectedPosition, keepPage: true, viewportFraction: 1),
itemBuilder: (BuildContext context, int itemIndex) {
return _buildCarouselItem(context, selectedIndex, itemIndex);
},
),
OR if you want to scroll the page after the button is clicked then, you can use jumpTo() method using PageController which is clearly mentioned below by another user: #android.
Currently there's 2 options to handle your request:
PageView.builder(
controller: _pageController,
itemCount: _list.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
_pageController.jumpToPage(index); // for regular jump
_pageController.animateToPage(_position, curve: Curves.decelerate, duration: Duration(milliseconds: 300)); // for animated jump. Requires a curve and a duration
},
child: Container();
);
}
),
You can use jumpTo() method to scroll position for PageView.
I have create one changePageViewPostion() method in below example:
import 'package:flutter/material.dart';
class MyPageView extends StatefulWidget {
createState() {
return StateKeeper();
}
}
class StateKeeper extends State<MyPageView> {
PageController controller = PageController(viewportFraction: 1, keepPage: true);
var currentPageValue = 0.0;
var mItemCount = 10;
#override
void initState() {
// TODO: implement initState
super.initState();
controller.addListener(() {
setState(() {
currentPageValue = controller.page;
});
});
}
void changePageViewPostion(int whichPage) {
if(controller != null){
whichPage = whichPage + 1; // because position will start from 0
double jumpPosition = MediaQuery.of(context).size.width / 2;
double orgPosition = MediaQuery.of(context).size.width / 2;
for(int i=0; i<mItemCount; i++){
controller.jumpTo(jumpPosition);
if(i==whichPage){
break;
}
jumpPosition = jumpPosition + orgPosition;
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PageView position change'),
),
body: PageView.builder(
controller: controller,
itemBuilder: (context, position) {
return Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Column(
children: <Widget>[
Center(
child: Text(
"Page " + (position + 1).toString(),
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
Align(
alignment: FractionalOffset.bottomCenter,
child: Padding(padding: EdgeInsets.only(bottom: 20),
child: FloatingActionButton(
elevation: 0.0,
child: new Icon(Icons.check),
backgroundColor: new Color(0xFFE57373),
onPressed: (){
changePageViewPostion(5);
}
),),
),
],
),
);
},
itemCount: mItemCount,
)
);
}
}
We can get current position with controller as below:
controller.addListener(() {
setState(() {
currentPageValue = controller.page.toInt();
print((currentPageValue + 1).toString());
});
});
Hope it helps :)
If you just want to scroll to the next page using a button, you can simply use the following method.
//Create a PageController variable
late PageController _pageController;
//Initialize the variable in the init method.
#override
void initState() {
_pageController = PageController(
initialPage: _activePage, keepPage: true, viewportFraction: 1);
super.initState();
}
//Use this nextPage() method in the onPressed() method.
onPressed: () {
setState(() {
_activePage < 2
? _activePage++
: Navigator.pushReplacementNamed(
context, LoginScreen.id);
});
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.decelerate,
);
}

Resources