Flutter convert dynamic listview to dynamic gridview - flutter

Good Day,
I am making great progress on my first app using flutter, some differences for sure, but found great help here. I have successfully produced a dynamic listview from JSON api call, I am trying to take that and convert it to a 2 column potrait gridview and 3 landscape. I have looked through the flutter gallery demo and the docs and can not seem to get a handle on the flow.
Anyone have some other examples or guidance to accomplish.
What I currently have is a ListView.builder calling an itembuilder that returns widgets of a leading icon, text and trailing icon. I want to convert it using the ICON to be the grid of graphics with onTap to another page as the listview does.
Any help or guidance would be great, thought it should be a simple conversion, but it has not been so far. I will attach some of the code below.
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new RefreshIndicator(
child: new ListView.builder(
itemBuilder: _itemBuilder,
itemCount: listcount,
),
onRefresh: _onRefresh,
));
}
Widget _itemBuilder(BuildContext context, int index) {
Specialties spec = getSpec(index);
return new SpecialtyWidget(spec: spec,);
}
Specialties getSpec(int index) {
return new Specialties(
mylist[index]['id'], mylist[index]['name'], mylist[index]['details'],
new Photo(mylist[index]['image'], mylist[index]['name'],
mylist[index]['name']));
}
}
class SpecialtyWidget extends StatefulWidget {
SpecialtyWidget({Key key, this.spec}) : super(key: key);
final Specialties spec;
#override
_SpecialtyWidgetState createState() => new _SpecialtyWidgetState();
}
class _SpecialtyWidgetState extends State<SpecialtyWidget> {
#override
Widget build(BuildContext context) {
return new Container(
height: 64.0,
width: 128.0,
child: new ListTile(
trailing: new Icon(Icons.arrow_right, color: Colors.green, size: 50.0,),
leading: new Image.network('http://$baseurl:8080/getimage/'+widget.spec.pic.assetName, fit: BoxFit.cover,),
title: new Text(
widget.spec.name,
style: new TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
),
onTap: _onTap,
),
);
Thanks

There's an example of GridView.count usage in the Flutter Gallery. You could use a LayoutBuilder or MediaQuery to determine whether the grid is portrait or landscape, and then choose a crossAxisCount count of 2 or 3 depending on what answer you get.

Related

Flutter: ListView - Green overlay instead of arrows in widget inspector

I'm creating a ListView with a builder function. I use the widget inspector to assess the any issues with the layout of the widgets.
Usually, the listView shows downwards green arrows as shown here:
[ListView layout][1]
However, in my current app, whenever I create a listView, it shows this green overlay on the listView. This creates artefacts with Image widget nested in stack; the images flicker when scrolling. [artefact layout][2]
This layout does look like it will take the space of 'drawer' in scaffold however, this page does not have a drawer, although all the other pages do.
Please find the code below for your reference.
const BlogListPage({Key? key}) : super(key: key);
#override
State<BlogListPage> createState() => _BlogListPageState();
}
class _BlogListPageState extends State<BlogListPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: ASAppBar(
title: const Text('Blogs'),
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Container(
margin:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
color: Colors.amber,
child: const SizedBox(
height: 100,
width: double.infinity,
),
);
},
));
}
} ```
[1]: https://i.stack.imgur.com/O7Pnc.png
[2]: https://i.stack.imgur.com/0g5HF.png

Flutter - Screen focus on a certain widget

I need help to do the following: when I press List 1, the screen focuses on List 1; I need the same for the rest of the options
This is the code for the example:
code
This behavior already exists in web pages but I haven't found this same behavior at the mobile app level. Thank you
Here is a small code snippet of something similar which might help you achieve you desired results.
By clicking the fab icon it will scroll down to item 35 within the ListView.
class MyHomePage extends StatelessWidget {
final _scrollController = ScrollController();
final _cardHeight = 200.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.orange,
onPressed: () => _animateToIndex(35),
child: Icon(Icons.add),
),
body: ListView.builder(
controller: _scrollController,
itemCount: 100,
itemBuilder: (_, i) => Container(
height: _cardHeight,
child: Card(
color: Colors.lightBlue,
child: Center(
child: Text("Scroll Item $i", style: TextStyle(fontSize: 28.0),),
),
),
),
),
);
}
_animateToIndex(index) {
_scrollController.animateTo(_cardHeight * index,
duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
}
You'll need to have a scrollable Widget (like ListView, SingleScrollableWidget) instead of a Column in ListSecondPage.
Then add a ScrollController to it and ListSecondPage should receive which button was tapped. Based on that selection you can scroll to the desired location with the ScrollController

Flutter - How to flip the previous card back using FlipCard

After days of search I'm getting help.
I work on a flutter application.
Context:
A grid view feeded with Json
-childs : GridTile with Flipcard in (https://pub.dev/packages/flip_card)
-On tap on GridTile there is a callback to get the selected Item and an animation because of the flipcard onTap
What I would:
When an item is aleready selected (flipcard flipped so we show the back of the card),
And I selected another item of the grid te(so flipcard of this itme also flipped)
I would like to flip back the old selected item Flipcard without rebuild the tree because I would lost the state of the new selected item.
I tried many thing. For example I tried to use GlobalKey on GridTiles to interract with after build but currentState is always null when I want to interact with.
I wonder what is the good practice in this case ?
I hope I was clear :) (I'm french)
Thank you the community!
.
Something to know...
It is possible to interract with the flipcard (child of gridtile) like this
(GlobalKey)
GlobalKey<FlipCardState> cardKey = GlobalKey<FlipCardState>();
#override
Widget build(BuildContext context) {
return FlipCard(
key: cardKey,
flipOnTouch: false,
front: Container(
child: RaisedButton(
onPressed: () => cardKey.currentState.toggleCard(),
child: Text('Toggle'),
),
),
back: Container(
child: Text('Back'),
),
);
}
I'm not sure if I understood your question, but here is an example of how you could use a GridView with FlipCards:
var cardKeys = Map<int, GlobalKey<FlipCardState>>();
GlobalKey<FlipCardState> lastFlipped;
Widget _buildFlipCard(String text, Color color, int index) {
return SizedBox(
height: 120.0,
child: Card(
color: color,
child: Center(
child:
Text(text, style: TextStyle(color: Colors.white, fontSize: 20.0)),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("FlipCards")),
body: GridView.builder(
itemCount: 20,
itemBuilder: (context, index) {
cardKeys.putIfAbsent(index, () => GlobalKey<FlipCardState>());
GlobalKey<FlipCardState> thisCard = cardKeys[index];
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FlipCardWithKeepAlive(
child: FlipCard(
flipOnTouch: false,
key: thisCard,
front: _buildFlipCard("$index", Colors.blue, index),
back: _buildFlipCard("$index", Colors.green, index),
onFlip: () {
if (lastFlipped != thisCard) {
lastFlipped?.currentState?.toggleCard();
lastFlipped = thisCard;
}
},
),
),
RaisedButton(
child: Text("Flip Card"),
onPressed: () => cardKeys[index].currentState.toggleCard(),
)
],
);
},
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
),
);
}
class FlipCardWithKeepAlive extends StatefulWidget {
final FlipCard child;
FlipCardWithKeepAlive({Key key, this.child}) : super(key: key);
#override
State<StatefulWidget> createState() => FlipCardWithKeepAliveState();
}
class FlipCardWithKeepAliveState extends State<FlipCardWithKeepAlive>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
#override
bool get wantKeepAlive => true;
}
You need to use a different key for each element of the list, I used a Map in this case.
I also wrapped the FlipCard with a custom FlipCardWithKeepAlive stateful widget that uses AutomaticKeepAliveClientMixin to keep alive the FlipCard while scrolling.
Edit: I updated the code so when you flip one card, the previous card flipped gets flipped back. Basically you need to save the last flipped card and when a new one is flipped, flip the last one and put the new one as last flipped.
The code will make both cards flip at the same time, if you want one card to wait the other use onFlipDone() instead of onFlip(), like this:
onFlipDone: (isFront) {
bool isFlipped = !isFront;
if (isFlipped && lastFlipped != thisCard) {
lastFlipped?.currentState?.toggleCard();
lastFlipped = thisCard;
}
}

Refresh widget or page in Flutter without ListView et al

I want refresh my page without having a scrollable content, i.e. without having a ListView et al.
When I want use RefreshIndicator, the documentation says it needs a scrollable widget like ListView.
But if I want to refresh and want to use the refresh animation of RefreshIndicator without using a ListView, GridView or any other scorllable widget, how can i do that?
You can simply wrap your content in a SingleChildScrollView, which will allow you to use a RefreshIndicator. In order to make the pull down to refresh interaction work, you will have to use AlwaysScrollableScrollPhysics as your content will most likely not cover more space than available without a scroll view:
RefreshIndicator(
onRefresh: () async {
// Handle refresh.
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: /* your content */,
),
);
You can just use GestureDetector, I have created a sample for you, but it's not perfect, you can customize it to your own needs, it just detects when you swipe from the top.
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
var refresh=false;
void refreshData(){
if(!refresh){
refresh=true;
print("Refreshing");
Future.delayed(Duration(seconds: 4),(){
refresh =false;
print("Refreshed");
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Test"),
centerTitle: true,
),
body: GestureDetector(
child: Container(
color: Colors.yellow,
height: double.infinity,
width: double.infinity,
child: Center(child: Text('TURN LIGHTS ON')),
),
onVerticalDragUpdate: (DragUpdateDetails details){
print("direction ${details.globalPosition.direction}");
print("distance ${details.globalPosition.distance}");
print("dy ${details.globalPosition.dy}");
if(details.globalPosition.direction < 1 && (details.globalPosition.dy >200 && details.globalPosition.dy < 250)){
refreshData();
}
},
));
}
}

Flutter Gallery Image Fullscreen Zoomable

I'm building a flutter app that displays products. Now, in the product details page I fetch product data along with product images as URLs using http package.
My question is what is the best way to represent product images to users? I have searched Google for a Flutter package similar or close to the web lightbox but couldn't find any.
Any advise?
In your detail page:
Show images using ListView in a Image widget with maybe 60x60 width/height as a thumbnail.
On click of a image -> Push a new page which has a swiper in it. While pushing you should provide the list of items and the currently selected one. Also later on check for Hero widget to have a smooth transition between those pages.
Image full screen page:
Place a PageView widget. Use the provided list in the itemBuilder and itemCount parameters.
It comes with gesture detection and animation between pages. In each of these pages use a Image widget to display the image as full size.
Since you have not provided any code sample, I won't be able to help you more but once you progress and have questions then it is better to ask with your current code.
I will share a complete sample using photo_view package. In your pubspec.yaml file add
dependencies:
photo_view: ^0.14.0
Then the main idea is to have a widget displaying something like a list of urls that you get for one product details (or anything else in your app).
The widget will display current image in a header, with a back button to pop the stack. You can swipe to display next and previous image.
class Gallery extends StatefulWidget {
final List<String> urlImages;
final int index;
final PageController pageController;
final Axis scrollDirection;
Gallery({
required this.urlImages,
this.index = 0,
this.scrollDirection = Axis.horizontal,
}) : pageController = PageController(initialPage: index);
#override
State<Gallery> createState() => _GalleryState();
}
class _GalleryState extends State<Gallery> {
late int currentIndex = widget.index;
#override
void initState() {
super.initState();
}
/// update ui header when user swipe
void _onPageChanged(int index) {
setState(() {
currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: AppColors.primaryColorTheme,
centerTitle: true,
title: Text(
'${currentIndex + 1} / ${widget.urlImages.length}',
style: const TextStyle(color: Colors.white),
),
iconTheme: const IconThemeData(color: Colors.white),
leading: IconButton( // back button here
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.arrow_back)),
),
body: Column(
children: <Widget>[
Expanded(
// create the gallery itself
child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
scrollDirection: widget.scrollDirection,
pageController: widget.pageController,
itemCount: widget.urlImages.length,
backgroundDecoration: const BoxDecoration(color: Colors.white70),
onPageChanged: _onPageChanged,
builder: _buildItem,
),
),
],
),
);
}
PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
final String item = widget.urlImages[index];
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(item), // we are using url so NetworkImage
initialScale: PhotoViewComputedScale.contained,
minScale: PhotoViewComputedScale.contained * (0.5 + index / 10),
maxScale: PhotoViewComputedScale.covered * 4.1,
heroAttributes: PhotoViewHeroAttributes(tag: item),
);
}
}
Finally, you have to start the widget in a new screen from your product list (eg if you have a link or a button "additionals images" or something like).
...
child: InkWell( // or use gesture detector as your convenience
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Gallery(urlImages : ["https://picsum.photos/200",
"https://picsum.photos/202"])));
},
...
You get it :). Hope that it will give you the main idea to achieve it.