Search code examples
flutterdartuser-interfacelistviewlayout

Unable to design a correct layout in flutter


I'm trying to make a task app with Flutter and Firebase. I'm not the best for designing so I took a design from Dribbble that i'm trying to copy for my app.

Here is the link of the design. Dribbble link

Here is the page I'm trying to copy : Page screenshot

Here is the code I did at the moment : (it's actually not working)

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(),
                      ],
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

I don't really know how to do the same layout cause I have to split the screen in 2 parts and there is ListView.builder (in my code there is only a ListView) who needs a fixed size.

Edit : Here is the looking of my app now (cause I wasn't able to find a solution by my own)App interface in emulator

Here is the full code of the page (don't mind of the Firebase code) :

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"],
                          );
                        },
                      );
                    },
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

How can I replace my GridView.builder and change some of the placement of other widget to get the looking that I want ?

If someone knows how i can copy the same page or just help me doing it, it would be very nice !


Solution

  • I would like to share the code that helped me solve my problem:

    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"],
                                ),
                              );
                            },
                          );
                        },
                      ),
                    ),
                  )
                ],
              ),
            ),
          ),
        );
      }
    }
    

    The Firebase code is not important; the layout is what matters most.

    Thanks to everyone who helped me with comments/answers, and thank you Ultranmus for taking the time to assist me!