0

I have a Flutter app which in one of the pages it presents a list of more than 200 items. How can I make it so that when the app is running in a desktop environment for example it displays more than one column dynamically and that when it runs on phones it displays a single columns or when it run on tablet in vertically orientation it displays one column and more than one column when the device is on horizontal orientation?

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'something here',
            ),
            Text(
              'something here',
            ),
            Text(
              'something here',
            ),
            Text(
              'something here',
            ),
          ],
        ),
      ),
    );
  }
}

This will produce a single column no matter the device or orientation.

Amani
  • 16,245
  • 29
  • 103
  • 153

2 Answers2

2

Try using GridView instead of Column, in that way you can adjust number of columns with crossAxisCount property of GridView. I am coming to that in a while.

Lets talk about how to adjust the number of columns dynamically depending upon the screen size. For example, I want single column in mobile, 2 columns in tab and 4 columns in desktop.

I'll create an enum named LayoutSize that represents different layout sizes. Also create an extension that returns number of columns depending upon the LayoutSize.

enum LayoutSize { small, medium, large }

extension LayoutSizeX on LayoutSize {
  int get noOfColumns {
    switch (this) {
      case LayoutSize.small:
        return 1;
      case LayoutSize.medium:
        return 2;
      case LayoutSize.large:
        return 4;
    }
  }
}

Now it's time to think about Breakpoints that will represent maximum width for different LayoutSize. You can change the numbers according to your requirements.

abstract class Breakpoints {
  static const double small = 760;
  static const double medium = 1644;
  static const double large = 1920;
}

Finally wrap up all these logic and create the UI. Here LayoutBuilder will serve the purpose of changing the number of columns whenever there is a change in constraints.

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      final screenWidth = MediaQuery.of(context).size.width;

      if (screenWidth <= Breakpoints.small) {
        return _buildMyWidget(LayoutSize.small);
      }

      if (screenWidth <= Breakpoints.medium) {
        return _buildMyWidget(LayoutSize.medium);
      }

      if (screenWidth <= Breakpoints.large) {
        return _buildMyWidget(LayoutSize.large);
      }

      return _buildMyWidget(LayoutSize.small);
    });
  }

  Widget _buildMyWidget(LayoutSize size) {
    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: size.noOfColumns,
      ),
      itemBuilder: (context, index) {
        return Container();
      },
    );
  }
}

I know this solution requires a little bit of extra coding, but believe me this is going to be very much efficient in long run, even in building the whole app responsive, not only a particular widget.

This is the minimal code you need to write in this particular case. But if you're interested in much generic way, here is a link to a gist.

thecodexhub
  • 151
  • 3
0

You can add your list Items conditionally in two ways:

Using if() statement:

  Column(
    children: [
      ListTile(
        ...
      ),
      if (_condition)
        Container(
          ...
        ),
    ],
  ),

Using condition like this:

Column (
  children: [
    x == 1 ? Widget1() : Widget2(),
  ],
)

What you need here is to figure out the OS and also the screen type, the Platform Class will give you some information about the OS:

import 'dart:io' show Platform;

if (Platform.isWindows) ...

and the MediaQuery class will give you useful information about the screen and sizes:

final isLandscape = (MediaQuery.of(context).orientation == Orientation.landscape);

There is no direct way to figure out that the Device is a Tablet or a Phone, but there are some tricks to get device type, here are some examples

You need a combination of these approaches to achive what you want

Mahmoud_Mehri
  • 1,643
  • 2
  • 20
  • 38
  • 1
    Please stop using MediaQuery for this. The preferred solution is LayoutBuilder. – Randal Schwartz Jan 31 '22 at 18:23
  • @RandalSchwartz It depends how you use it – Mahmoud_Mehri Jan 31 '22 at 18:39
  • @Mahmoud_Mehri from what I read on https://docs.flutter.dev/development/ui/layout/adaptive-responsive, LayoutBuilder is certainly the better choice for this case. We only need to change the number of columns, solely on the width of these columns parent widget, we don't care about anything else. – Daniel Wu Feb 23 '23 at 05:29