TabBarView с динамической высотой и отступом

введите описание изображения здесь

У меня есть 3 вкладки с динамическим представлением сетки, но моя проблема в том, что, когда я тестировал его на физическом устройстве, на одном из них конец представления сетки выглядел завершенным, но на другом меньшем устройстве (720 x 1280) последняя строка the gridview Текст не был виден, поэтому я подумал добавить отступ внизу, но, конечно, на маленьком устройстве это выглядит нормально

введите описание изображения здесь

И добавляем этот отступ в GridView.Builder ...:

padding: EdgeInsets.only(right: 20.0, left: 20.0, bottom: 100),

введите описание изображения здесь

но в других устройствах с более высоким разрешением из-за этого отступ внизу не выглядит одинаково, а здесь отступ больше

введите описание изображения здесь

Как я могу сделать это динамическое заполнение, чтобы оно было одинаковым и сетка была полностью видна на всех устройствах?

Кроме того, я хочу добавить, что мои TabBarViews находятся внутри контейнера высотой 500, и я не знаю, может ли это быть проблемой, потому что я чувствую, что он статичен, и мне ничего не нравится, я бы хотел, чтобы контейнер был только доводить до конца экрана с самого начала (под вкладками), и я тоже не знаю, как это сделать.

Кроме того, я видел, что при тестировании всегда указывалось, что столбец под safearea и scaffold имел проблемы hasSize, и я всегда получал много исключений высоты, пытаясь исправить это, в shop_scree_page.dart, я мало знаю о дизайне и, может быть, я очень ошибаюсь.

shop_scree_page.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:plantsapp/screens/all_tab.dart';
import 'package:plantsapp/screens/indoor_page.dart';
import 'package:plantsapp/screens/outdoor_tab.dart';
import 'package:plantsapp/screens/top_tab.dart';
import 'package:plantsapp/services/authentication_service.dart';

class ShopScreen extends StatefulWidget {
  @override
  _ShopScreenState createState() => _ShopScreenState();
}

class _ShopScreenState extends State<ShopScreen>
    with SingleTickerProviderStateMixin {
  TabController _tabController;
  PageController _pageController;
  GlobalKey<ScaffoldState> _drawerKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    _tabController = TabController(initialIndex: 0, length: 4, vsync: this);
    _pageController = PageController(initialPage: 0, viewportFraction: 0.8);
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> containers = [
      new TopTab(),
      new OutdoorTab(),
      new IndoorTab(),
      new AllTab(),
    ];

    AuthenticationService authServ = new AuthenticationService();
    return Scaffold(
      key: _drawerKey,
      drawer: new Drawer(),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.logout),
        backgroundColor: Colors.black,
        onPressed: () {
          //context.read<AuthenticationService>()
          authServ.signOut();
        },
      ),
      drawerEnableOpenDragGesture: false,
      body: SafeArea(
        child: Column(
          children: <Widget>[
            //ICONOS MENU Y CART
            Container(
              margin: EdgeInsets.only(top: 20),
              child: Padding(
                padding: EdgeInsets.symmetric(horizontal: 30.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    IconButton(
                      icon: Icon(Icons.menu, size: 30.0, color: Colors.grey),
                      onPressed: (() => _drawerKey.currentState.openDrawer()),
                    ),
                    Spacer(),
                    IconButton(
                        icon: Icon(Icons.add),
                        color: Colors.black,
                        iconSize: 32,
                        onPressed: () {
                          Navigator.pushNamed(context, 'createplantpost');
                        }),
                    IconButton(
                        icon: Icon(Icons.shopping_cart),
                        color: Colors.black,
                        onPressed: () {}),
                  ],
                ),
              ),
            ),
            SizedBox(height: 20.0),

            //TITULO "TOP PICKS"
            Expanded(
              child: Container(
                width: double.infinity,
                child: SingleChildScrollView(
                  child: Column(
                    children: [
                      Container(
                        alignment: Alignment.centerLeft,
                        child: Padding(
                          padding: EdgeInsets.only(left: 20.0),
                          child: Text(
                            'Top Picks',
                            style: TextStyle(
                                fontSize: 24.0, fontWeight: FontWeight.w600),
                          ),
                        ),
                      ),
                      _tabs(),
                      _tabsView(containers),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

 Widget _tabs() {
    return TabBar(
      controller: _tabController,
      indicatorColor: Colors.transparent,
      labelColor: Colors.black,
      unselectedLabelColor: Colors.grey.withOpacity(0.6),
      labelPadding: EdgeInsets.symmetric(horizontal: 35.0),
      isScrollable: true,
      tabs: <Widget>[
        Tab(
            child: Text('Top',
                style: TextStyle(
                  fontSize: 16.0,
                  fontWeight: FontWeight.w600,
                ))),
        Tab(
            child: Text('Outdoor',
                style: TextStyle(
                  fontSize: 16.0,
                  fontWeight: FontWeight.w600,
                ))),
        Tab(
            child: Text('Indoor',
                style: TextStyle(
                  fontSize: 16.0,
                  fontWeight: FontWeight.w600,
                ))),
        Tab(
            child: Text('All',
                style: TextStyle(
                  fontSize: 16.0,
                  fontWeight: FontWeight.w600,
                ))),
      ],
    );
  }

  Widget _tabsView(containers) {
    final size = MediaQuery.of(context).size;
    return  Container(
      height: 500,
      child: TabBarView(
            physics: NeverScrollableScrollPhysics(),
            controller: _tabController,
            children: containers
      ),
    );
  }
}

outdoor_tab.dart

import 'package:flutter/material.dart';
import 'package:plantsapp/models/plant_model.dart';
import 'package:plantsapp/providers/products_provider.dart';
import 'package:plantsapp/screens/plant_screen.dart';
import 'package:plantsapp/widgets/snapshots_alerts.dart';

class OutdoorTab extends StatefulWidget {
  const OutdoorTab({Key key}) : super(key: key);

  @override
  _OutdoorTabState createState() => _OutdoorTabState();
}

class _OutdoorTabState extends State<OutdoorTab> {
  @override
  Widget build(BuildContext context) {
    ProductsProvider productsProvider = new ProductsProvider();

    return Scaffold(
      body: streamBuilderCards(productsProvider),
    );
  }

  Widget streamBuilderCards(ProductsProvider productsProvider) {
    final size = MediaQuery.of(context).size;
    return StreamBuilder(
      stream: productsProvider.getPostOutdoor(context),
      builder:
          (BuildContext context, AsyncSnapshot<List<PlantModel>> snapshot) {
        if (snapshot.hasError) {
          return snapshotMsgError(snapshot);
        }
        if (!snapshot.hasData) {
          return snapshotCircularProgressIndicator();
        }
        List<PlantModel> posts = snapshot.data;

        return GridView.builder(
          padding: EdgeInsets.only(
              right: 20.0, left: 20.0, bottom: 100),
          itemCount: posts.length,
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              mainAxisSpacing: 20,
              crossAxisSpacing: 20,
              childAspectRatio: 0.60),
          itemBuilder: (context, index) => ItemCard(plants: posts[index]),
        );
      },
    );
  }
}

class ItemCard extends StatelessWidget {
  final PlantModel plants;
  final Function press;

  const ItemCard({
    Key key,
    this.press,
    this.plants,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (_) => PlantScreen(plant: plants),
          ),
        );
      },
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
              height: 198,
              width: 175,
              decoration: BoxDecoration(
                  //color: Colors.blueAccent,
                  borderRadius: BorderRadius.circular(16)),
              child: Hero(
                tag: plants.imageUrl,
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(16),
                  child: (plants.imageUrl == null)
                      ? Image(
                          image: AssetImage('assets/images/no-image.png'),
                          fit: BoxFit.cover)
                      : FadeInImage(
                          image: NetworkImage(plants.imageUrl),
                          placeholder:
                              AssetImage('assets/images/image-loading.gif'),
                          fit: BoxFit.cover,
                        ), /*Image(
                  image: NetworkImage(plants.imageUrl),
                  fit: BoxFit.cover,
                )*/
                ),
              ) //Image.asset(plants.imageUrl),
              ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 4.0),
            child: Text(
              plants.name,
              style: TextStyle(color: Colors.grey),
              overflow: TextOverflow.ellipsis,
            ),
          ),
          Text(
            '\$${plants.price}',
            style: TextStyle(fontWeight: FontWeight.bold),
          )
        ],
      ),
    );
  }
}

person Kondos    schedule 19.01.2021    source источник


Ответы (1)


Вы можете сделать это с помощью динамического заполнения по размеру экрана. Для опыта:

var screenHeight = MediaQuery.of(context).size.height;
var screenWidth = MediaQuery.of(context).size.width;

padding: EdgeInsets.only(right: screenWidth * 0.2, left: screenWidth * 0.2, bottom: screenHeight * 0.15)
person Miraç Kılıç    schedule 20.01.2021