Невозможно создать правильный макет во флаттере

Я пытаюсь создать приложение для задач с помощью Flutter и Firebase. Я не лучший специалист в дизайне, поэтому взял дизайн с Dribbble и пытаюсь скопировать его для своего приложения.

Вот ссылка на дизайн. Ссылка на Dribbble

Вот страница, которую я пытаюсь скопировать: Скриншот страницы

Вот код, который я сделал на данный момент: (на самом деле он не работает)

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final String userID = FirebaseAuth.instance.currentUser!.uid;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.only(top: 25.0),
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.only(left: 25),
                child: Align(
                  alignment: Alignment.centerLeft,
                  child: StreamBuilder(
                    stream: FirebaseFirestore.instance
                        .collection("Users")
                        .doc(userID)
                        .snapshots(),
                    builder: (context, snapshot) {
                      if (snapshot.connectionState == ConnectionState.waiting) {
                        return const Center(child: CircularProgressIndicator());
                      }
                      if (snapshot.hasError) {
                        return const Text("Erreur");
                      }
                      return Text.rich(
                        TextSpan(
                          text: "Bonjour,\n",
                          style: GoogleFonts.exo2(
                            fontSize: 40,
                            height: 1.25,
                          ),
                          children: [
                            TextSpan(
                              text: snapshot.data!.data()!["name"],
                              style: const TextStyle(
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ],
                        ),
                      );
                    },
                  ),
                ),
              ),
              Row(
                children: [
                  // Column de gauche
                  SizedBox(
                    width: MediaQuery.of(context).size.width / 2,
                    child: ListView(
                      children: const [],
                    ),
                  ),

                  // Column de droite
                  SizedBox(
                    width: MediaQuery.of(context).size.width / 2,
                    child: Column(
                      children: [
                        Container(),
                      ],
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Я действительно не знаю, как сделать тот же макет, потому что мне нужно разделить экран на две части, и есть ListView.builder (в моем коде есть только ListView), которому нужен фиксированный размер.

Редактировать : Вот как сейчас выглядит мое приложение (потому что я не смог найти решение самостоятельно)

Вот полный код страницы (не обращайте внимания на код Firebase):

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:db_todoapp/components/my_task_tile.dart';
import 'package:db_todoapp/pages/account_page.dart';
import 'package:db_todoapp/utilities/task_service.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:shimmer/shimmer.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final String userID = FirebaseAuth.instance.currentUser!.uid;
  final TaskService _ts = TaskService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.only(top: 25.0),
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.only(left: 25),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    // Message de bienvenue
                    StreamBuilder(
                      stream: FirebaseFirestore.instance
                          .collection("Users")
                          .doc(userID)
                          .snapshots(),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState ==
                            ConnectionState.waiting) {
                          return Shimmer.fromColors(
                            baseColor: const Color.fromRGBO(224, 224, 224, 1),
                            highlightColor: Colors.grey,
                            child: Text.rich(
                              TextSpan(
                                text: "Bonjour,\n",
                                style: GoogleFonts.exo2(
                                  fontSize: 40,
                                  height: 1.25,
                                ),
                                children: const [
                                  TextSpan(
                                    text: "Nom",
                                    style: TextStyle(
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          );
                        }
                        if (snapshot.hasError) {
                          return const Text("Erreur");
                        }
                        return GestureDetector(
                          onTap: () {
                            Navigator.push(
                                context,
                                MaterialPageRoute(
                                    builder: (context) => AccountPage()));
                          },
                          child: Text.rich(
                            TextSpan(
                              text: "Bonjour,\n",
                              style: GoogleFonts.exo2(
                                fontSize: 40,
                                height: 1.25,
                              ),
                              children: [
                                TextSpan(
                                  text: snapshot.data!.data()!["name"],
                                  style: const TextStyle(
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        );
                      },
                    ),

                    // Bouton pour ajouter une tâche
                    GestureDetector(
                      onTap: () {
                        _ts.addTask(context);
                      },
                      child: Container(
                        padding: const EdgeInsets.all(25.0),
                        decoration: const BoxDecoration(
                          border: Border(
                            top: BorderSide(
                              width: 1.5,
                              color: Color.fromRGBO(189, 189, 189, 1),
                            ),
                            bottom: BorderSide(
                              width: 1.5,
                              color: Color.fromRGBO(189, 189, 189, 1),
                            ),
                            left: BorderSide(
                              width: 1.5,
                              color: Color.fromRGBO(189, 189, 189, 1),
                            ),
                          ),
                          borderRadius: BorderRadius.horizontal(
                            left: Radius.circular(18.0),
                          ),
                        ),
                        child: Column(
                          children: [
                            // Icon +
                            const Icon(
                              Icons.add,
                              size: 30,
                            ),

                            // Texte
                            Text(
                              "Ajouter une tâche",
                              style: GoogleFonts.exo2(
                                fontSize: 15,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ],
                        ),
                      ),
                    )
                  ],
                ),
              ),
              const Padding(
                padding: EdgeInsets.all(25.0),
                child: Divider(),
              ),
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 25.0),
                  child: StreamBuilder(
                    stream: FirebaseFirestore.instance
                        .collection("Users")
                        .doc(userID)
                        .collection("Tasks")
                        .snapshots(),
                    builder: (context, snapshot) {
                      if (snapshot.connectionState == ConnectionState.waiting) {
                        return Shimmer.fromColors(
                          baseColor: const Color.fromRGBO(224, 224, 224, 1),
                          highlightColor: Colors.grey,
                          child: GridView.builder(
                            gridDelegate:
                                const SliverGridDelegateWithFixedCrossAxisCount(
                              crossAxisCount: 2,
                            ),
                            itemCount: null,
                            itemBuilder: (context, index) {
                              return MyTaskTile(
                                taskTitle: "Task Title",
                                taskIndex: index,
                                isTaskDone: false,
                                taskTheme: null,
                              );
                            },
                          ),
                        );
                      }
                      if (snapshot.data!.docs.isEmpty ||
                          snapshot.data == null) {
                        return Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: [
                              // Illustration
                              Image.asset("assets/no_task.png"),

                              // Texte
                              Text(
                                "Vous n'avez aucune tâche.",
                                style: GoogleFonts.exo2(
                                  fontSize: 30,
                                  fontWeight: FontWeight.bold,
                                ),
                                textAlign: TextAlign.center,
                              ),
                            ],
                          ),
                        );
                      }
                      return GridView.builder(
                        gridDelegate:
                            const SliverGridDelegateWithFixedCrossAxisCount(
                          crossAxisCount: 2,
                        ),
                        itemCount: snapshot.data?.docs.length ?? 0,
                        itemBuilder: (context, index) {
                          return MyTaskTile(
                            taskTitle: snapshot.data!.docs[index]["title"],
                            taskIndex: index,
                            isTaskDone: snapshot.data!.docs[index]["state"],
                            taskTheme: snapshot.data!.docs[index]["theme"],
                          );
                        },
                      );
                    },
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

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

Если кто-то знает, как я могу скопировать ту же страницу, или просто поможет мне это сделать, было бы очень здорово!

Определите и объявите поток вне метода сборки. В противном случае при каждой сборке виджета будет добавлен новый прослушиватель снимков. И удалите прослушиватель в распоряжении. Для макета, подобного вашему, попробуйте использовать flutter_staggered_grid_view: ^0.7.0 или используйте два списка для двух списков. Создайте их физику NeverScrollableScrollPhysics и оберните ее другим представлением списка или представлением одиночной детской прокрутки. Но всегда есть лучший метод, поэтому попробуйте его.

Ultranmus 24.04.2024 21:22

Как бы вы тут использовали GridView, мне надо добавить кнопку "Добавить задачу" (посмотрите на дизайн).

Furiy 25.04.2024 11:34

@Fuiry Я говорю о flutter_staggered_grid_view, а не о GridView. Сначала ознакомьтесь с этим пакетом.

Ultranmus 25.04.2024 12:27

Если я использую «flutter_staggered_grid_view» и укажу кнопку «Добавить задачу» для второго индекса моего построителя, моя кнопка будет прокручиваться, но мне нужна кнопка «Исправить». Поправьте меня, если я ошибаюсь (и извините, если я не совсем понимаю, я не из англоязычной страны)

Furiy 25.04.2024 18:54

Итак, вам нужно сделать плавающую кнопку. Тогда мое единственное предложение — использовать размер в шахматной сетке. И покройте всю сетку стопкой. Определите размер этого размера и соответствующим образом расположите кнопку добавления задачи в стеке.

Ultranmus 25.04.2024 21:11

Используйте MasonryGridView.count от flutter_staggered_grid_view. Сделайте crossAxisCount равным 2. И укажите размер элемента. В индекс 1 мы добавим поле размера с предопределенной высотой. Для ширины это будет ширина экрана/2 — интервал и отступ. Теперь у вас есть сайзбокс и его размер. Теперь покройте эту каменную решетку стекой. Поместите кнопку добавления задачи в правом верхнем углу с тем же размером, что и предопределенный размер. И он произведет почти то, что вы хотели. Вам нужно настроить несколько вещей и поэкспериментировать с ними в соответствии с вашими потребностями.

Ultranmus 25.04.2024 21:20

Я только что еще раз отредактировал свой вопрос. Забудьте о проблеме с высотой моего контейнера, с которой я столкнулся, мы исправим ее позже. Итак, как вы можете видеть, мне просто нужно кое-что отредактировать в моем коде, но основа уже готова. Я просто не понимаю, как можно без ошибок оставить пустое место позади позиционированной кнопки. Я пробовал кое-что, но не нашел решения. Надеюсь, вы снова сможете мне помочь. Мне очень жаль, что я заставил вас терять время. Я начинаю с Flutter и изо всех сил стараюсь стать лучше.

Furiy 30.04.2024 20:09

Вы использовали GridView. В ответе я упомянул об использовании каменной сетки. А для позиционированной кнопки вы можете обернуть виджет внутри позиционированного виджета другим контейнером, придать ему отступы и цвет. Но это будет выглядеть не очень хорошо, потому что это наложение, и пространство вокруг него будет выглядеть плохо, когда за ним находятся прокручиваемые элементы. И согласно вашему дизайну, кнопка добавления задачи должна находиться поверх других элементов при прокрутке сетки. Я добавлю для вас макет макета, когда найду время, но я бы посоветовал указать упомянутую вами ошибку.

Ultranmus 30.04.2024 20:29

«У меня возникла идея. Я планирую добавить кнопку «Добавить задачу» в первый индекс MasonryGridView. Таким образом, все задачи будут генерироваться с их индексами — 1, что не позволит мне пропустить первая задача моих пользователей. Я буду держать вас в курсе и изменять свои вопросы по мере поиска решения».

Furiy 01.05.2024 13:28

Я поделился ответом на свой вопрос. Посмотрите, если вам интересно. Я воспользовался вашими предложениями и адаптировал их под свои конкретные нужды. Еще раз спасибо за ваше время :)

Furiy 01.05.2024 14:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
107
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

попробуйте использовать GridView.builder, возможно, это решит вашу проблему

Как бы вы использовали здесь GridView.builder? Потому что мне нужно поставить кнопку "Добавить задачу".

Furiy 25.04.2024 11:33

тогда вам нужно написать функцию, например, если индекс == 1, затем показать мне контейнер с задачей «Добавить» и вызвать его после построителя

EyoGroup 25.04.2024 21:05

Да, я понимаю, но если я помещу кнопку «Добавить задачу» прямо в GridView.builder, например, под индексом 1, кнопка будет прокручиваться. Мне нужна фиксированная кнопка в моем интерфейсе. И если я скажу «никогда не прокручивать» для GridView.builder, мой список задач также не будет прокручиваться.

Furiy 26.04.2024 11:21

GridView.builder — хорошее решение, но я думаю, что для такого точного вида вам нужно иметь два списка рядом друг с другом.

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

пример того, что я хотел:

пример моего кода:

Expanded(
                child: Container(
                  margin:EdgeInsets.only(top: 10) ,
                  child: ListView(
                    //listview has default top padding
                      padding: EdgeInsets.only(top: 0),
                      children: [
                        //dates
                        Container(
                          child: Row(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [


                             Container(
                                margin: EdgeInsets.only(top: 20),
                                child: Column(
                                  children: [
                                    for (var destination in destenationsList)
                                    // your codee
                                    // your codee
                                  ],
                                ),
                              ),
                              //destinations
                              Container(
                                width: s.width*0.8,
                                margin: EdgeInsets.only(top: 20),
                                child: Column(
                                  children: [
                                    for (var destination in destenationsList)
                                    // your codee
                                    // your codee
                                      
                                  ],
                                ),
                              ),

                            ],
                          ),
                        ),]
                  ),
                ),
              ),

Да, я думал то же самое, поэтому попробовал использовать ListView.builder, но у меня возникли некоторые ошибки, и я не смог их исправить самостоятельно, поэтому я прошу помощи здесь.

Furiy 25.04.2024 18:49

@Furiy У меня почти похожий пример, я отредактирую ответ

Adam 26.04.2024 11:55

Как я уже упоминал в своем комментарии, использование flutter_staggered_grid_view вместе со стеком может создать такой же дизайн. Проверьте код ниже, он дает то, что вы хотели. Но вам нужно соответствующим образом установить размер в коде.

Stack(
        children: [
          MasonryGridView.count(
            padding: const EdgeInsets.all(15.0),
            crossAxisCount: 2,
            mainAxisSpacing: 16,
            crossAxisSpacing: 16,
            itemBuilder: (context, index) {
              if (index == 1) {
                return const SizedBox(
                  height: 150,
                );
              }
              return Container(
                height: 300,
                decoration: BoxDecoration(
                  color: Colors.red,
                  borderRadius:  BorderRadius.circular(20.0),
                ),
              );
            },
          ),
          Positioned(
            right: 0,
            top: 15,
            child: Container(
              height: 150,
              width: MediaQuery.sizeOf(context).width * 0.5 - 8, // half of screen size - half of cross axis padding in MasonryGridView will give its width
              decoration: BoxDecoration(
                color: Colors.white,
                border: Border.all(color: Colors.grey),
                borderRadius: const BorderRadius.only(
                    topLeft: Radius.circular(20),
                    bottomLeft: Radius.circular(20)),
              ),
              alignment: Alignment.center,
              child: const Icon(Icons.add),
            ),
          )
        ],
      )

Результат

Хороший ! Это почти то, что я хочу! Попробую воспроизвести это для своего случая. Но есть ли способ избежать наличия красного контейнера за кнопкой?

Furiy 27.04.2024 13:12

Красный — это просто цвет, который я придал контейнеру. Вы можете дать ему все, что захотите. Или вы имеете в виду что-то другое, не показывая красный контейнер?

Ultranmus 27.04.2024 20:00

Я имею в виду, что если вы прокрутите вниз, у вас не будет пробела за кнопкой, потому что вы создали пробел в MasonryGridView.count. Итак, есть ли способ всегда иметь пустую область за кнопкой? Это был мой вопрос. Думаю, можно добавить позиционированный пустой контейнер в стек сразу за кнопкой, но я еще не пробовал, потому что работаю над чем-то другим в приложении.

Furiy 27.04.2024 21:50

Если вы не хотите, чтобы красный контейнер отображался поверх кнопки при прокрутке, либо оберните кнопку другим контейнером, закрывающим верхнюю часть, и установите для нее белый цвет. Или не добавляйте отступы в верхней части GridView. Все зависит от вашего варианта использования.

Ultranmus 27.04.2024 22:13

Я столкнулся с проблемой. Я попытался установить пробел в первом индексе GridView, но это означает, что первый элемент моего MansoryGridView не появится. Итак, я попытался поместить кнопку «Позиционировано» в контейнер с некоторым отступом и установил цвет контейнера такой же, как и фон моего Scaffold, но, по моему мнению, это выглядит очень плохо. Я столкнулся с другой проблемой: какую бы высоту я ни установил для TaskTile, она остается неизменной в моем MasonryGridView. (Посмотрите мой пост, я отредактирую его, чтобы показать вам).

Furiy 28.04.2024 21:49

Было бы хорошо, если бы вы показали какое-нибудь изображение/видео вашего проекта. А представление каменной сетки рассчитывает размер дочернего элемента в зависимости от его содержимого. Если вы не назначите ему какой-либо размер, он примет размер содержимого, но если вы зададите ему некоторый размер, он примет его. И я действительно не понял, что вы имели в виду, говоря, что вы назначаете высоту контейнеру и не имеете над ней контроля.

Ultranmus 28.04.2024 22:17

На самом деле меня нет дома, но я постараюсь объяснить лучше, когда смогу.

Furiy 30.04.2024 08:17
Ответ принят как подходящий

Я хотел бы поделиться кодом, который помог мне решить мою проблему:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:db_todoapp/components/my_task_tile.dart';
import 'package:db_todoapp/pages/account_page.dart';
import 'package:db_todoapp/utilities/task_service.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:shimmer/shimmer.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final String userID = FirebaseAuth.instance.currentUser!.uid;
  final TaskService _ts = TaskService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.only(top: 25.0),
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.only(left: 25),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    // Message de bienvenue
                    StreamBuilder(
                      stream: FirebaseFirestore.instance
                          .collection("Users")
                          .doc(userID)
                          .snapshots(),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState ==
                            ConnectionState.waiting) {
                          return Shimmer.fromColors(
                            baseColor: const Color.fromRGBO(224, 224, 224, 1),
                            highlightColor: Colors.grey,
                            child: Text.rich(
                              TextSpan(
                                text: "Bonjour,\n",
                                style: GoogleFonts.exo2(
                                  fontSize: 40,
                                  height: 1.25,
                                ),
                                children: const [
                                  TextSpan(
                                    text: "Nom",
                                    style: TextStyle(
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          );
                        }
                        if (snapshot.hasError) {
                          return const Text("Erreur");
                        }
                        return GestureDetector(
                          onTap: () {
                            Navigator.push(
                                context,
                                MaterialPageRoute(
                                    builder: (context) => AccountPage()));
                          },
                          child: Text.rich(
                            TextSpan(
                              text: "Bonjour,\n",
                              style: GoogleFonts.exo2(
                                fontSize: 40,
                                height: 1.25,
                              ),
                              children: [
                                TextSpan(
                                  text: snapshot.data!.data()!["name"],
                                  style: const TextStyle(
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        );
                      },
                    ),

                    StreamBuilder(
                      stream: FirebaseFirestore.instance
                          .collection("Users")
                          .doc(userID)
                          .collection("Tasks")
                          .snapshots(),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState ==
                            ConnectionState.waiting) {
                          return const SizedBox();
                        }
                        if (snapshot.data == null ||
                            snapshot.data!.docs.isEmpty) {
                          return GestureDetector(
                            onTap: () {
                              _ts.addTask(context);
                            },
                            child: Container(
                              padding: const EdgeInsets.all(25.0),
                              decoration: const BoxDecoration(
                                border: Border(
                                  top: BorderSide(
                                    width: 1.5,
                                    color: Color.fromRGBO(189, 189, 189, 1),
                                  ),
                                  bottom: BorderSide(
                                    width: 1.5,
                                    color: Color.fromRGBO(189, 189, 189, 1),
                                  ),
                                  left: BorderSide(
                                    width: 1.5,
                                    color: Color.fromRGBO(189, 189, 189, 1),
                                  ),
                                ),
                                borderRadius: BorderRadius.horizontal(
                                  left: Radius.circular(18.0),
                                ),
                              ),
                              child: Column(
                                children: [
                                  // Icon +
                                  const Icon(
                                    Icons.add,
                                    size: 30,
                                  ),

                                  // Texte
                                  Text(
                                    "Ajouter une tâche",
                                    style: GoogleFonts.exo2(
                                      fontSize: 15,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          );
                        }
                        return const SizedBox();
                      },
                    ),
                  ],
                ),
              ),
              const Padding(
                padding: EdgeInsets.all(25.0),
                child: Divider(),
              ),
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.only(left: 10.0),
                  child: StreamBuilder(
                    stream: FirebaseFirestore.instance
                        .collection("Users")
                        .doc(userID)
                        .collection("Tasks")
                        .snapshots(),
                    builder: (context, snapshot) {
                      if (snapshot.connectionState ==
                          ConnectionState.waiting) {}
                      if (snapshot.data == null ||
                          snapshot.data!.docs.isEmpty) {
                        return Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: [
                              // Illustration
                              Image.asset("assets/no_task.png"),

                              // Texte
                              Text(
                                "Vous n'avez aucune tâche.",
                                style: GoogleFonts.exo2(
                                  fontSize: 30,
                                  fontWeight: FontWeight.bold,
                                ),
                                textAlign: TextAlign.center,
                              ),
                            ],
                          ),
                        );
                      }
                      return MasonryGridView.count(
                        itemCount: snapshot.data!.docs.length + 1,
                        crossAxisCount: 2,
                        itemBuilder: (context, index) {
                          if (index == 0) {
                            return Padding(
                              padding: const EdgeInsets.only(right: 10.0),
                              child: MyTaskTile(
                                taskTitle: snapshot.data!.docs[index]["title"],
                                taskIndex: index,
                                isTaskDone: snapshot.data!.docs[index]["state"],
                                taskTheme: snapshot.data!.docs[index]["theme"],
                              ),
                            );
                          }
                          if (index == 1) {
                            return Padding(
                              padding: const EdgeInsets.only(bottom: 10.0),
                              child: GestureDetector(
                                onTap: () {
                                  _ts.addTask(context);
                                },
                                child: Container(
                                  padding: const EdgeInsets.all(25.0),
                                  decoration: const BoxDecoration(
                                    border: Border(
                                      top: BorderSide(
                                        width: 1.5,
                                        color: Color.fromRGBO(189, 189, 189, 1),
                                      ),
                                      bottom: BorderSide(
                                        width: 1.5,
                                        color: Color.fromRGBO(189, 189, 189, 1),
                                      ),
                                      left: BorderSide(
                                        width: 1.5,
                                        color: Color.fromRGBO(189, 189, 189, 1),
                                      ),
                                    ),
                                    borderRadius: BorderRadius.horizontal(
                                      left: Radius.circular(18.0),
                                    ),
                                  ),
                                  child: Column(
                                    children: [
                                      // Icon +
                                      const Icon(
                                        Icons.add,
                                        size: 30,
                                      ),

                                      // Texte
                                      Text(
                                        "Ajouter une tâche",
                                        style: GoogleFonts.exo2(
                                          fontSize: 15,
                                          fontWeight: FontWeight.bold,
                                        ),
                                      ),
                                    ],
                                  ),
                                ),
                              ),
                            );
                          }
                          return Padding(
                            padding: const EdgeInsets.only(right: 10.0),
                            child: MyTaskTile(
                              taskTitle: snapshot.data!.docs[index - 1]
                                  ["title"],
                              taskIndex: index - 1,
                              isTaskDone: snapshot.data!.docs[index - 1]
                                  ["state"],
                              taskTheme: snapshot.data!.docs[index - 1]
                                  ["theme"],
                            ),
                          );
                        },
                      );
                    },
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Код Firebase не важен; макет — это самое главное.

Спасибо всем, кто помогал мне комментариями/ответами, и спасибо Ultranmus за то, что нашли время мне помочь!

Просто любопытно, вы хотели, чтобы задача добавления накладывалась, верно? Но в вашем коде кнопка является частью сетки и прокручивается.

Ultranmus 02.05.2024 05:35

Этот отзыв предоставляется как часть задачи проверки: Благодарственные письма в stackoverflow не приветствуются. Если вы нашли комментарий полезным, пожалуйста, проголосуйте за него.

Dan R 02.05.2024 15:20

Да, вначале я хотел, чтобы кнопка была поверх всего остального, но когда я пытался это сделать, результат меня не удовлетворил. Поэтому я решил просто разместить кнопку в MadonryGridView.

Furiy 03.05.2024 17:03

Другие вопросы по теме