web-dev-qa-db-fra.com

Ajuster la hauteur des enfants GridView en fonction du contenu dynamique dans le flutter

Comment mettre en œuvre cette vue complexe dans le flutter?

J'essaie d'implémenter une GridView avec n colonnes et l'enfant doit avoir un certain rapport d'aspect (disons 1,3), mais sa taille doit être égale (contenu intégré dans la terminologie Android). 

Je suis coincé parce que je comprends très bien que le childAspectRatio:1.3 (par défaut: 1) de GridView dispose toujours l'enfant dans le même rapport d'aspect, mais pas dans le contenu dynamique.

Remarque: Child doit agrandir la hauteur en fonction de la hauteur de l'image

Cas d'utilisation: / J'essaie d'implémenter une vue similaire à celle ci-dessous, dans laquelle l'image est enveloppée height = wrap content afin que, dans le cas où une image avec une hauteur étirée puisse être belle et former une structure semblable à StaggeredGridView.

Sample Image

7
Vipul Asri

Edit: J'ai ajouté le constructeur StaggeredTile.fit dans le 0.2.0. Avec cela, vous devriez être capable de construire votre application ;-).

 Dynamic tile sizes

Premier commentaire: Pour l'instant avec StaggeredGridView , la présentation et le rendu des enfants sont totalement indépendants. Donc, comme @rmtmckenzie l'a dit, vous devrez obtenir la taille de l'image pour créer vos tuiles . Vous pouvez ensuite utiliser le constructeur StaggeredTile.count avec une valeur double pour le paramètre mainAxisCellCount: new StaggeredTile.count(x, x*h/w) (où h est la hauteur de votre image et w largeur, de sorte que les carreaux avec le même rapport d’aspect que votre image.

Ce que vous souhaitez accomplir nécessitera davantage de travail car vous souhaitez disposer d’une zone contenant des informations sous l’image. Pour cela, je pense que vous devrez calculer la largeur réelle de votre mosaïque avant de la créer et utiliser le constructeur StaggeredTile.extent.

Je comprends que ce n’est pas idéal et je travaille actuellement sur une nouvelle façon de créer la mise en page. J'espère que cela vous aidera à construire des scénarios comme le vôtre.

7
Romain Rastel

Pour l’un quelconque des moyens relativement simples de le faire (c’est-à-dire sans une compréhension approfondie du fonctionnement de la disposition dans Flutter), vous devrez obtenir la taille des images avant de construire quoi que ce soit. Ceci est une réponse qui décrit comment procéder en utilisant ImageProvier et ImageStream .

Vous pouvez ensuite utiliser l'exemple de/aziza de flutter_staggered_grid_view une fois que vous connaissez les dimensions de base des images.

Une alternative pourrait consister à stocker la taille de l’image ou au moins le format de l’image où vous stockez la liste des images/urls (je ne sais pas comment vous remplissez la liste des images, donc je ne peux pas vous aider là-bas).

Si vous voulez qu'il soit entièrement basé sur la taille des images et ne ressemble pas du tout à une grille, vous pourrez peut-être le faire avec un widget Flow . Cependant, il y a une mise en garde: je pense qu'il ne gérera pas beaucoup d'articles car il devrait mettre tous les enfants dehors à chaque fois, mais je peux me tromper à ce sujet. Si vous ne possédez pas une quantité énorme d'éléments, vous pouvez utiliser Flow + a SingleChildScrollView pour la partie défilante.

Si vous allez avoir une grande quantité d'éléments (et/ou souhaitez effectuer un chargement dynamique de nouveaux éléments), vous devrez peut-être faire quelque chose avec CustomMultiChildLayout - Je pense que ce serait plus efficace, mais vous Il faudrait quand même faire quelque chose pour connaître la taille des images.

Une dernière solution possible (je ne sais pas exactement comment cela fonctionnerait) serait d’avoir deux vues défilables côte à côte et de synchroniser leurs positions. Vous devez toutefois définir shrinkwrap = true pour pouvoir faire défiler l'écran et connaître la hauteur de chaque image pour pouvoir choisir le côté dans lequel placer chaque image.

J'espère que cela vous aidera au moins à démarrer!

2
rmtmckenzie

Tout d’abord, laissez-moi vous raconter comment je me suis retrouvé ici: 

Dans mon application, je voulais une vue en grille pour afficher mes cartes d'annonce et toutes les données provenant de la base de données du serveur. Les images proviennent du serveur et les images sont de tailles différentes. J'ai utilisé FutureBuilder pour mapper ces données sur GridView. J'ai d'abord essayé d'utiliser:

double cardWidth = MediaQuery.of(context).size.width / 3.3;
double cardHeight = MediaQuery.of(context).size.height / 3.6;
//....
GridView.count(
  childAspectRatio: cardWidth / cardHeight,
  //..

Comme vous pouvez le constater, toutes les cartes ne seront pas dynamiques. Je suis venue ici comme vous et j'ai essayé d'utiliser toutes les réponses. Celles-ci sont excellentes et vous devez vous attaquer un peu pour comprendre comment, mais aucune de ces réponses n'a complètement résolu mon problème.

En utilisant @RomainRastel, vous répondez et grâce à son paquet StaggeredGridView . Je devais utiliser StaggeredGridView.count comme constructeur pour mapper toutes les cartes et pour la propriété staggeredTiles, je devais de nouveau mapper toutes les cartes et les ajouter pour chaque StaggeredTile.fit(2).

Je suis sûr que vous ne l'avez pas encore compris. Essayons donc un exemple simple pour ne pas avoir à vous rendre ailleurs pour trouver une réponse:

Ajoutez d’abord la dépendance à pubspec.yaml; la version actuelle est 0.2.5. Vous pouvez commander le dernier ici .

dependencies:
 flutter_staggered_grid_view: ^0.2.5

Si vous récupérez des données sur Internet ou si vous allez copier-coller cet exemple, vous devez également ajouter cette dépendance: http: ^0.12.0.

import 'package:flutter/material.Dart';

//this is what you need to have for flexible grid
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.Dart';

//below two imports for fetching data from somewhere on the internet
import 'Dart:convert';
import 'package:http/http.Dart' as http;

//boilerplate that you use everywhere
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flexible GridView",
      home: HomePage(),
    );
  }
}

//here is the flexible grid in FutureBuilder that map each and every item and add to a gridview with ad card
class HomePage extends StatelessWidget {
  //this is should be somewhere else but to keep things simple for you,
  Future<List> fetchAds() async {
    //the link you want to data from, goes inside get
    final response = await http
        .get('https://blasanka.github.io/watch-ads/lib/data/ads.json');

    if (response.statusCode == 200) return json.decode(response.body);
    return [];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Dynamic height GridView Demo"),
      ),
      body: FutureBuilder<List>(
          future: fetchAds(),
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            if (snapshot.hasData) {
              return new Padding(
                padding: const EdgeInsets.all(4.0),
                //this is what you actually need
                child: new StaggeredGridView.count(
                  crossAxisCount: 4, // I only need two card horizontally
                  padding: const EdgeInsets.all(2.0),
                  children: snapshot.data.map<Widget>((item) {
                    //Do you need to go somewhere when you tap on this card, wrap using InkWell and add your route
                    return new AdCard(item);
                  }).toList(),

                  //Here is the place that we are getting flexible/ dynamic card for various images
                  staggeredTiles: snapshot.data
                      .map<StaggeredTile>((_) => StaggeredTile.fit(2))
                      .toList(),
                  mainAxisSpacing: 3.0,
                  crossAxisSpacing: 4.0, // add some space
                ),
              );
            } else {
              return Center(
                  child:
                      new CircularProgressIndicator()); // If there are no data show this
            }
          }),
    );
  }
}

//This is actually not need to be a StatefulWidget but in case, I have it
class AdCard extends StatefulWidget {
  AdCard(this.ad);

  final ad;

  _AdCardState createState() => _AdCardState();
}

class _AdCardState extends State<AdCard> {
  //to keep things readable
  var _ad;
  String _imageUrl;
  String _title;
  String _price;
  String _location;

  void initState() {
    setState(() {
      _ad = widget.ad;
      //if values are not null only we need to show them
      _imageUrl = (_ad['imageUrl'] != '')
          ? _ad['imageUrl']
          : 'https://uae.microless.com/cdn/no_image.jpg';
      _title = (_ad['title'] != '') ? _ad['title'] : '';
      _price = (_ad['price'] != '') ? _ad['price'] : '';
      _location = (_ad['location'] != '') ? _ad['location'] : '';
    });

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      semanticContainer: false,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(4.0)),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Image.network(_imageUrl),
          Text(_title),
          Text('\$ $_price'),
          Text(_location),
        ],
      ),
    );
  }
}

Si vous avez un problème, voici exemple complet dans un référentiel git .

Flutter flexible grid view example

Bonne chance!

0
Blasanka

Il y a deux choses ici:

  1. Il y a un paquetage existant pour faire cette mise en page

  2. Pour que les images paraissent bien, utilisez BoxFit.cover dans le widget DecorationImage.

Il y a des tonnes d'exemples dans le package repo here }

Je viens d'utiliser l'un des exemples et de le modifier pour inclure des images:

enter image description here

class GridViewExample extends StatefulWidget {
  @override
  _GridViewExampleState createState() => new _GridViewExampleState();
}

class _GridViewExampleState extends State<GridViewExample> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Padding(
        padding: const EdgeInsets.all(8.0),
        child: new StaggeredGridView.countBuilder(
  crossAxisCount: 4,
  itemCount: 8,
  itemBuilder: (BuildContext context, int index) => new Container(
        decoration: new BoxDecoration(
          image: new DecorationImage(
            image: new NetworkImage("https://i.imgur.com/EVTkpZL.jpg"),
            fit: BoxFit.cover
          )
        )

        ),

  staggeredTileBuilder: (int index) =>
      new StaggeredTile.count(2, index.isEven ? 2 : 1),
  mainAxisSpacing: 4.0,
  crossAxisSpacing: 4.0,
),),

    );
  }
}
0
aziza