web-dev-qa-db-ja.com

Flutterの動的コンテンツに従ってGridViewの子の高さを調整します

フラッターにこの複雑なビューを実装する方法?

私はn列でGridViewを実装しようとしていますが、子は特定のアスペクト比(たとえば1.3)でなければなりませんが、子の高さは(Android =用語)。

FasとしてGridViewのchildAspectRatio:1.3(デフォルト:1)は常に同じアスペクト比で子をレイアウトしますが、動的コンテンツはレイアウトしません。

注:子は画像の高さに応じて高さを拡張する必要があります

ユースケース:以下のようなビューを実装しようとしています。このビューでは画像がラップされますheight = wrap contentこれは、高さが引き伸ばされた画像が見た目がよく、StaggeredGridViewのような構造を形成できるようにするためです。

Sample Image

12
Vipul Asri

編集:0.2.0にコンストラクタ_StaggeredTile.fit_を追加しました。それにより、アプリをビルドできるはずです;-)。

Dynamic tile sizes

最初のコメント:とりあえず StaggeredGridView では、レイアウトと子のレンダリングは完全に独立しています。 @rmtmckenzieが言ったように、タイルを作成するには画像サイズを取得する必要があります。次に、mainAxisCellCountパラメーターにdouble値を持つ_StaggeredTile.count_コンストラクターを使用できます:new StaggeredTile.count(x, x*h/w)(ここで、hは画像の高さ、wその幅:タイルの縦横比が画像と同じになるようにします。

達成したいことは、画像の下に何らかの情報を含む領域が必要なため、さらに作業が必要になります。そのためには、タイルを作成する前にタイルの実際の幅を計算し、_StaggeredTile.extent_コンストラクターを使用する必要があると思います。

これは理想的ではないことを理解しており、現在、レイアウトを作成する新しい方法に取り組んでいます。あなたのようなシナリオを構築するのに役立つことを願っています。

11
Romain Rastel

これを行う比較的簡単な方法(つまり、フラッターのレイアウトがどのように機能するかを深く理解していない場合)の場合、何かを構築する前に画像のサイズを取得する必要があります。 これは答えですImageProvier および ImageStream を使用してそれを行う方法を説明しています。

画像の基本的な寸法がわかれば、@ azizaの flutter_staggered_grid_view の例を使用できます。

別の方法は、画像/ URLのリストを保存する場所に画像サイズまたは少なくともアスペクト比を保存することです(画像のリストにどのように入力するのかわかりませんので、そこにあなたを助けることはできません)。

画像のサイズに完全に基づいており、グリッドのようなものではない場合は、 Flow ウィジェットを使用して行うことができます。ただし、フローには注意点があります-毎回すべての子供を配置する必要があるため、大量のアイテムをうまく処理できないと思いますが、それについて間違っている可能性があります。大量のアイテムがない場合は、スクロール部分にFlow + a SingleChildScrollView を使用できます。

大量のアイテムを使用する場合(および/または新しいアイテムの動的読み込みなどを行う場合)、 CustomMultiChildLayout を使用して何かを行う必要がある場合があります。効率的ですが、画像のサイズを知るためにまだ何かをする必要があります。

最後の可能な解決策(これがどのように機能するか正確にはわかりません)は、2つのスクロール可能なビューを並べて配置し、それらの位置を同期することです。ただし、スクロールできるようにshrinkwrap = trueを設定する必要があります。また、各画像をどちらの側に配置するかを決定できるように、各画像の高さを知る必要があります。

少なくともあなたが始めるのに役立つことを願っています!

2
rmtmckenzie

まず、私がここでどのように終わったかについてお話ししましょう。

私のアプリケーションでは、グリッドビューに広告カードを表示し、サーバーデータベースから取得したすべてのデータと画像はサーバーから取得し、画像のサイズは異なっていました。 FutureBuilderを使用して、これらのデータをGridViewにマップしました。最初に使用しようとしました:

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

ご覧のとおり、すべてのカードに対して動的ではありません。私はあなたのようにここに来て、それらの素晴らしい答えをすべて使おうとしました。あなたはその方法を理解するために少し取り組む必要がありますが、それらの答えのどれもが私の問題を完全に解決しました。

@ RomainRastel回答を使用し、彼の StaggeredGridView パッケージに感謝します。 _StaggeredGridView.count_をコンストラクターとして使用して、すべてのカードをマップし、staggeredTilesプロパティに対して、すべてのカードを再度マップし、StaggeredTile.fit(2)ごとに追加する必要がありました。

まだ手に入らなかったと思いますので、答えを見つけるために他の場所に行く必要がないように、簡単な例を試してみましょう。

最初に_pubspec.yaml_に依存関係を追加します。現在のバージョンは_0.2.5_です。最新のものをチェックアウトできます here

_dependencies:
 flutter_staggered_grid_view: ^0.2.5
_

インターネットからデータを取得する場合、またはこの例をコピーして貼り付ける場合は、この依存関係を追加する必要があります:_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),
        ],
      ),
    );
  }
}
_

問題がある場合、ここに gitリポジトリの完全な例 があります。

Flutter flexible grid view example

がんばろう!

0
Blasanka

ここには2つのことがあります。

  1. このようなレイアウトを行うための 既存のパッケージ があります

  2. 画像の見栄えを良くするにはBoxFit.coverDecorationImageウィジェット上。

ここにパッケージリポジトリ にはたくさんの例があります

私はちょうど例を使用して、写真を含めるように修正しました:

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