Layout master-detail in Flutter

Mattepuffo's logo
Layout master-detail in Flutter

Layout master-detail in Flutter

Quello che vogliamo creare è un classico layout master-detail in Flutter, senza l'ausilio di qualche sorta di designer (sempre se ne esistono).

Cominciamo col creare una nostra classe "POJO":

import 'package:meta/meta.dart';

class Nota {
  Nota({
    @required this.id,
    @required this.titolo,
    @required this.testo,
  });

  final int id;
  final String titolo;
  final String testo;
}

final List<Nota> note = <Nota>[
  Nota(id: 1, titolo: "UNO", testo: "TESTO 1"),
  Nota(id: 2, titolo: "DUE", testo: "TESTO 2")
];

Qui creiamo anche una lista che useremo dopo.

Poi creiamo una ListView che le visualizzi tutte:

import 'package:mp_notes/nota.dart';
import 'package:flutter/material.dart';

class NotaList extends StatelessWidget {
  NotaList({
    @required this.notaSelectedCallback,
    this.selectedNota,
  });

  final ValueChanged<Nota> notaSelectedCallback;
  final Nota selectedNota;

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: note.map((nota) {
        return ListTile(
          title: Text(nota.titolo),
          onTap: () => notaSelectedCallback(nota),
          selected: selectedNota == nota,
        );
      }).toList(),
    );
  }
}

Il passo successivo è creare una classe che visualizzi una nota singola:

import 'package:mp_notes/nota.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';

class NotaDettagli extends StatelessWidget {
  NotaDettagli({@required this.isTablet, @required this.nota});

  final bool isTablet;
  final Nota nota;

  @override
  Widget build(BuildContext context) {
    final TextTheme textTheme = Theme.of(context).textTheme;
    final Widget content = Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          nota?.titolo ?? 'Nessun elemento selezionato',
          style: textTheme.headline,
        ),
        Text(
          nota?.testo ?? '',
          style: textTheme.subhead,
        ),
      ],
    );

    if (isTablet) {
      return Center(child: content);
    }

    return Scaffold(
      appBar: AppBar(
        title: Text(nota.titolo),
      ),
      body: Center(child: content),
    );
  }
}

A questo punto abbiamo costruito alcune parti del layout; adesso vediamo la parte più importante:

import 'package:mp_notes/nota.dart';
import 'package:mp_notes/nota_dettagli.dart';
import 'package:mp_notes/nota_list.dart';
import 'package:flutter/material.dart';

class MasterDetailLayout extends StatefulWidget {
  @override
  NotaMasterDetailLayoutState createState() => NotaMasterDetailLayoutState();
}

class NotaMasterDetailLayoutState extends State<MasterDetailLayout> {
  static const int tabletBreakpoint = 600;

  Nota selectedNota;

  Widget mobileLayout() {
    return NotaList(
      notaSelectedCallback: (nota) {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (BuildContext context) {
              return NotaDettagli(
                isTablet: false,
                nota: nota,
              );
            },
          ),
        );
      },
    );
  }

  Widget tabletLayout() {
    return Row(
      children: <Widget>[
        Flexible(
          flex: 1,
          child: Material(
            elevation: 4.0,
            child: NotaList(
              notaSelectedCallback: (nota) {
                setState(() {
                  selectedNota = nota;
                });
              },
              selectedNota: selectedNota,
            ),
          ),
        ),
        Flexible(
          flex: 3,
          child: NotaDettagli(
            isTablet: true,
            nota: selectedNota,
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    Widget content;
    var shortestSide = MediaQuery.of(context).size.shortestSide;

    if (shortestSide < tabletBreakpoint) {
      content = mobileLayout();
    } else {
      content = tabletLayout();
    }

    return Scaffold(
      appBar: AppBar(
        title: Text('MP Note'),
      ),
      body: content,
    );
  }
}

Qui incapsuliamo i precedenti pezzi di layout, andando anche a vedere se siamo su un dispositivo tablet o meno (o almeno ci proviamo, vista la quantità di dispositivi diversi che troviamo oggi in giro).

Questo il file Dart che rappresenta l'entrypoint della app (main.dart):

import 'package:mp_notes/master_detail_layout.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MP Note',
      theme: ThemeData(
        primarySwatch: Colors.orange,
      ),
      home: MasterDetailLayout(),
    );
  }
}

Enjoy!


Condividi

Commentami!