Responsive Design Menggunakan MediaQuery

Jika Anda pernah develop web, tentu istilah MediaQuery tidak asing lagi. Dengan menggunakan MediaQuery, kita bisa mendapatkan informasi ukuran suatu object.

Mari kita coba implementasikan untuk aplikasi expense tracker. Buka file main.dart, lalu tambahkan code berikut.

final _screenSize = MediaQuery.of(context).size.height -
    _appBar.preferredSize.height -
    MediaQuery.of(context).padding.top;
  • MediaQuery.of(context).padding.top adalah untuk mendapatkan tinggi system status bar diatas (yang berisi info jaringan, jam, batterai dan lainnya)
  • MediaQuery.of(context).size.height adalah informasi tinggi total dari device.
  • _appBar.preferredSize.height, adalah tiggi dari appBar. Namun untuk mendapatkan tinggi appBar, kita harus membuat variable _appBar yang berisi widget appBar.
final _appBar = AppBar(
  title: Text('Expense Tracker'),
  actions: <Widget>[
    IconButton(
      icon: Icon(Icons.add),
      onPressed: () => _showFormExpense(context),
    )
  ],
);

Perhitungan diatas akan digunakan saat menampilkan widget Chart dan Expense List

return Scaffold(
  appBar: _appBar,
...
...
...

// variable _screenSize digunakan disini

          Container(height: _screenSize * 0.3, child: Chart(_rcntTrx)),
          Container(
              height: _screenSize * 0.7,
              child: ExpenseList(_trans, _delExpense)),
        ]),
  ),

Berikut isi akhir dari main.dart

import 'package:flutter/material.dart';

import './models/trans.dart';
import './widgets/expense_list.dart';
import './widgets/form_expense.dart';
import './widgets/chart.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Expense Tracker',
      theme: ThemeData(
          primaryColor: Colors.red,
          accentColor: Colors.red[400],
          textTheme: TextTheme(
            bodyText2: TextStyle(
              color: Colors.red,
              fontFamily: 'Quicksand',
            ),
          ),
          fontFamily: 'OpenSans'),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final List<Trans> _trans = [
  ];

  List<Trans> get _rcntTrx {
    return _trans.where((element) {
      return element.txdate.isAfter(DateTime.now().subtract(Duration(days: 7)));
    }).toList();
  }

  void _addNewExpense(String newTitle, double newAmount, DateTime newDate) {
    final newExpense = Trans(
        id: DateTime.now().toString(),
        title: newTitle,
        amount: newAmount,
        txdate: newDate);

    setState(() {
      _trans.add(newExpense);
    });
  }

  void _showFormExpense(BuildContext ctx) {
    showModalBottomSheet(
      context: ctx,
      builder: (_) {
        return FormExpense(_addNewExpense);
      },
    );
  }

  void _delExpense(String expId) {
    setState(() {
      _trans.removeWhere((element) {
        return element.id == expId;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    final _appBar = AppBar(
      title: Text('Expense Tracker'),
      actions: <Widget>[
        IconButton(
          icon: Icon(Icons.add),
          onPressed: () => _showFormExpense(context),
        )
      ],
    );

    final _screenSize = MediaQuery.of(context).size.height -
        _appBar.preferredSize.height -
        MediaQuery.of(context).padding.top;

    return Scaffold(
      appBar: _appBar,
      body: SingleChildScrollView(
        child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Container(height: _screenSize * 0.3, child: Chart(_rcntTrx)),
              Container(
                  height: _screenSize * 0.7,
                  child: ExpenseList(_trans, _delExpense)),
            ]),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () => _showFormExpense(context),
      ),
    );
  }
}

Karena ukuran widget sudah kita hitung pada main.dart, maka ukuran hardcode pada expense_list.dart dapat kita buang.

Widget build(BuildContext context) {

//bagian container bisa kita buang
  return Container(
    height: 300,
    child: _trans.isEmpty
        ? Column(
            children: <Widget>[

Berikut hasil akhir expense_list.dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import '../models/trans.dart';

class ExpenseList extends StatelessWidget {
  final List<Trans> _trans;
  final Function _delExp;

  ExpenseList(this._trans, this._delExp);

  @override
  Widget build(BuildContext context) {
    return _trans.isEmpty
        ? Column(
            children: <Widget>[
              Text(
                'No Expense...',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              Container(
                height: 200,
                child: Image.asset(
                  'assets/img/empty-icon.png',
                  fit: BoxFit.cover,
                ),
              ),
            ],
          )
        : ListView.builder(
            itemCount: _trans.length,
            itemBuilder: (ctx, idx) {
              return Card(
                margin: EdgeInsets.symmetric(vertical: 7, horizontal: 4),
                elevation: 2,
                child: ListTile(
                  leading: CircleAvatar(
                    radius: 50,
                    child: FittedBox(
                      child: Text(
                        '\$${_trans[idx].amount}',
                      ),
                    ),
                  ),
                  title: Text(_trans[idx].title),
                  subtitle: Text(DateFormat.yMMMd().format(_trans[idx].txdate)),
                  trailing: IconButton(
                    icon: Icon(Icons.delete),
                    color: Theme.of(context).errorColor,
                    onPressed: () => _delExp(_trans[idx].id),
                  ),
                ),
              );
            },
          );
  }
}
Sharing is caring:

Leave a Comment