Post Detail

Post #97

Flutter Jour Quatre Part 2

Flutter Jour Quatre

Fin PDF 3 (Responsive)

Le Fichier main.dart

  1/// main.dart
  2
  3import 'package:depenses/widgets/transaction_list.dart';
  4import 'package:flutter/material.dart';
  5
  6import './models/transaction.dart';
  7
  8import './widgets/new_transaction.dart';
  9import './widgets/chart.dart';
 10
 11void main() {
 12  runApp(MyApp());
 13}
 14
 15class MyApp extends StatelessWidget {
 16  @override
 17  Widget build(BuildContext context) {
 18    return MaterialApp(
 19      theme: ThemeData(
 20        primarySwatch: Colors.purple,
 21        accentColor: Colors.amber,
 22        errorColor: Colors.red,
 23        fontFamily: 'Quicksand',
 24        appBarTheme: AppBarTheme(
 25          textTheme: ThemeData.light().textTheme.copyWith(
 26                title: TextStyle(
 27                  fontFamily: 'OpenSans',
 28                  fontWeight: FontWeight.bold,
 29                  fontSize: 20,
 30                ),
 31                button: TextStyle(
 32                  color: Colors.white,
 33                ),
 34              ),
 35        ),
 36      ),
 37      home: MyHomePage(),
 38    );
 39  }
 40}
 41
 42class MyHomePage extends StatefulWidget {
 43  @override
 44  _MyHomePageState createState() => _MyHomePageState();
 45}
 46
 47class _MyHomePageState extends State<MyHomePage> {
 48  final titleController = TextEditingController();
 49  final amountController = TextEditingController();
 50
 51  bool _showChart = false;
 52
 53  List<Transaction> get _recentTransactions {
 54    return _userTransactions.where((transaction) {
 55      return transaction.date.isAfter(
 56        DateTime.now().subtract(
 57          Duration(days: 7),
 58        ),
 59      );
 60    }).toList();
 61  }
 62
 63  final List<Transaction> _userTransactions = [
 64    Transaction(
 65      id: 't1',
 66      title: 'Chaussures',
 67      amount: 169.99,
 68      date: DateTime.now(),
 69    ),
 70    Transaction(
 71      id: 't2',
 72      title: 'Courses',
 73      amount: 128.50,
 74      date: DateTime.now(),
 75    ),
 76  ];
 77
 78  void _addNewTransaction(
 79      String newTitle, double newAmount, DateTime chosenDate) {
 80    final newTransaction = Transaction(
 81        title: newTitle,
 82        amount: newAmount,
 83        date: chosenDate,
 84        id: DateTime.now().toString());
 85
 86    setState(() {
 87      _userTransactions.add(newTransaction);
 88    });
 89  }
 90
 91  void _deleteTransaction(String id) {
 92    setState(() {
 93      _userTransactions.removeWhere((transaction) => transaction.id == id);
 94    });
 95  }
 96
 97  void _startAddNewTransaction(BuildContext context) {
 98    showModalBottomSheet(
 99        context: context,
100        builder: (_) {
101          return NewTransaction(_addNewTransaction);
102        });
103  }
104
105  @override
106  Widget build(BuildContext context) {
107    final mediaQuery = MediaQuery.of(context);
108    final isLandscape = mediaQuery.orientation == Orientation.landscape;
109
110    final appBar = AppBar(
111      title: const Text('Personal Expenses'),
112      actions: <Widget>[
113        IconButton(
114            icon: const Icon(Icons.add),
115            onPressed: () => _startAddNewTransaction(context)),
116      ],
117    );
118
119    final textListWidget = Container(
120      height: (MediaQuery.of(context).size.height -
121              appBar.preferredSize.height -
122              MediaQuery.of(context).padding.top) *
123          0.7,
124      child: TransactionList(_userTransactions, _deleteTransaction),
125    );
126
127    return Scaffold(
128      appBar: appBar,
129      body: SingleChildScrollView(
130        child: Column(
131          mainAxisAlignment: MainAxisAlignment.start,
132          crossAxisAlignment: CrossAxisAlignment.stretch,
133          children: <Widget>[
134            if (isLandscape)
135              Row(
136                mainAxisAlignment: MainAxisAlignment.center,
137                children: <Widget>[
138                  Text(
139                    'Show Chart',
140                  ),
141                  Switch(
142                    value: _showChart,
143                    onChanged: (value) {
144                      setState(() {
145                        _showChart = value;
146                      });
147                    },
148                  ),
149                ],
150              ),
151            if (!isLandscape)
152              Container(
153                height: (MediaQuery.of(context).size.height -
154                        appBar.preferredSize.height -
155                        MediaQuery.of(context).padding.top) *
156                    0.3,
157                child: Chart(_recentTransactions),
158              ),
159            if (!isLandscape) textListWidget,
160            if (isLandscape)
161              _showChart
162                  ? Container(
163                      height: (MediaQuery.of(context).size.height -
164                              appBar.preferredSize.height -
165                              MediaQuery.of(context).padding.top) *
166                          0.7,
167                      child: Chart(_recentTransactions),
168                    )
169                  : textListWidget,
170          ],
171        ),
172      ),
173      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
174      floatingActionButton: FloatingActionButton(
175        child: Icon(
176          Icons.add,
177        ),
178        onPressed: () => _startAddNewTransaction(context),
179      ),
180    );
181  }
182}

Le Fichier new_transaction.dart

  1/// ./widgets/new_transaction.dart
  2
  3import 'package:flutter/material.dart';
  4import 'package:intl/intl.dart';
  5
  6class NewTransaction extends StatefulWidget {
  7  final Function addNewTransaction;
  8
  9  NewTransaction(this.addNewTransaction);
 10
 11  @override
 12  _NewTransactionState createState() => _NewTransactionState();
 13}
 14
 15class _NewTransactionState extends State<NewTransaction> {
 16  final _titleController = TextEditingController();
 17  final _amountController = TextEditingController();
 18  DateTime _selectedDate;
 19
 20  void submitData() {
 21    if (_amountController.text.isEmpty) {
 22      return;
 23    }
 24    final enteredTitle = _titleController.text;
 25    final enteredAmount = double.parse(_amountController.text);
 26    if (enteredTitle.isEmpty || enteredAmount <= 0 || _selectedDate == null) {
 27      return;
 28    }
 29    widget.addNewTransaction(
 30      enteredTitle,
 31      enteredAmount,
 32      _selectedDate,
 33    );
 34    Navigator.of(context).pop();
 35  }
 36
 37  void _presentDatePicker() {
 38    showDatePicker(
 39      context: context,
 40      initialDate: DateTime.now(),
 41      firstDate: DateTime(DateTime.now().year),
 42      lastDate: DateTime.now(),
 43    ).then((pickedDate) {
 44      if (pickedDate == null) {
 45        return;
 46      }
 47      setState(() {
 48        _selectedDate = pickedDate;
 49      });
 50    });
 51  }
 52
 53  @override
 54  Widget build(BuildContext context) {
 55    return SingleChildScrollView(
 56      child: Card(
 57        child: Container(
 58          padding: EdgeInsets.only(
 59            top: 10,
 60            left: 10,
 61            right: 10,
 62            bottom: MediaQuery.of(context).viewInsets.bottom + 10,
 63          ),
 64          child: Column(
 65            crossAxisAlignment: CrossAxisAlignment.end,
 66            children: <Widget>[
 67              TextField(
 68                decoration: InputDecoration(
 69                  labelText: 'Titre',
 70                ),
 71                controller: _titleController,
 72                onSubmitted: (_) => submitData(),
 73              ),
 74              TextField(
 75                decoration: InputDecoration(
 76                  labelText: 'Montant',
 77                ),
 78                controller: _amountController,
 79                keyboardType: TextInputType.number,
 80                onSubmitted: (_) => submitData(),
 81              ),
 82              Container(
 83                height: 70,
 84                child: Row(
 85                  children: <Widget>[
 86                    Text(
 87                      _selectedDate == null
 88                          ? 'Aucune date choisie!'
 89                          : 'Date sélectionnée: ${DateFormat('d.M.y').format(_selectedDate)}',
 90                    ),
 91                    FlatButton(
 92                      textColor: Theme.of(context).primaryColor,
 93                      child: Text(
 94                        'Choisir une date',
 95                        style: TextStyle(
 96                          fontWeight: FontWeight.bold,
 97                        ),
 98                      ),
 99                      onPressed: _presentDatePicker,
100                    ),
101                  ],
102                ),
103              ),
104              RaisedButton(
105                child: Text(
106                  'Add Transaction',
107                ),
108                color: Theme.of(context).primaryColor,
109                textColor: Theme.of(context).textTheme.button.color,
110                onPressed: submitData,
111              ),
112            ],
113          ),
114        ),
115      ),
116    );
117  }
118}

Le Fichier transaction_list.dart

 1/// ./widgets/transaction_list.dart
 2
 3import 'package:flutter/material.dart';
 4import 'package:intl/intl.dart';
 5
 6import '../models/transaction.dart';
 7
 8class TransactionList extends StatelessWidget {
 9  final List<Transaction> transactions;
10  final Function deleteTransaction;
11
12  TransactionList(this.transactions, this.deleteTransaction);
13
14  @override
15  Widget build(BuildContext context) {
16    return ListView.builder(
17      itemBuilder: (context, index) {
18        return Card(
19          elevation: 5,
20          margin: EdgeInsets.symmetric(
21            vertical: 8,
22            horizontal: 5,
23          ),
24          child: ListTile(
25            leading: CircleAvatar(
26              radius: 30,
27              child: Padding(
28                padding: EdgeInsets.all(6),
29                child: FittedBox(
30                  child: Text(
31                    '\$${transactions[index].amount.toStringAsFixed(2)}',
32                  ),
33                ),
34              ),
35            ),
36            title: Text(
37              transactions[index].title,
38              style: Theme.of(context).textTheme.title,
39            ),
40            subtitle: Text(
41              DateFormat('d.M.y').format(transactions[index].date),
42            ),
43            trailing: MediaQuery.of(context).size.width > 460
44                ? FlatButton.icon(
45                    icon: Icon(Icons.delete),
46                    label: Text('Delete'),
47                    textColor: Theme.of(context).errorColor,
48                    onPressed: () => deleteTransaction(transactions[index].id),
49                  )
50                : IconButton(
51                    icon: Icon(Icons.delete),
52                    color: Theme.of(context).errorColor,
53                    onPressed: () => deleteTransaction(transactions[index].id),
54                  ),
55          ),
56        );
57      },
58      itemCount: transactions.length,
59    );
60  }
61}

Le Fichier char.dart

 1/// ./widgets/char.dart
 2
 3import 'package:flutter/material.dart';
 4import 'package:intl/intl.dart';
 5
 6import '../models/transaction.dart';
 7import './char_bar.dart';
 8
 9class Chart extends StatelessWidget {
10  final List<Transaction> recentTransactions;
11
12  Chart(this.recentTransactions);
13
14  List<Map<String, Object>> get groupedTransactionValues {
15    return List.generate(7, (index) {
16      final weekDay = DateTime.now().subtract(
17        Duration(days: index),
18      );
19
20      var totalSum = 0.0;
21      for (var i = 0; i < recentTransactions.length; i++) {
22        if (recentTransactions[i].date.day == weekDay.day &&
23            recentTransactions[i].date.month == weekDay.month &&
24            recentTransactions[i].date.year == weekDay.year) {
25          totalSum += recentTransactions[i].amount;
26        }
27      }
28
29      return {
30        'day': DateFormat.E().format(weekDay).substring(0, 1),
31        'amount': totalSum
32      };
33    }).reversed.toList();
34  }
35
36  double get totalSpending {
37    return groupedTransactionValues.fold(0.0, (sum, item) {
38      return sum + item['amount'];
39    });
40  }
41
42  @override
43  Widget build(BuildContext context) {
44    print(groupedTransactionValues);
45
46    return Card(
47      elevation: 6,
48      margin: EdgeInsets.all(20),
49      child: Padding(
50        padding: EdgeInsets.all(10),
51        child: Row(
52          children: groupedTransactionValues.map((data) {
53            return Flexible(
54              fit: FlexFit.tight,
55              child: ChartBar(
56                data['day'],
57                data['amount'],
58                (data['amount'] as double) / totalSpending,
59              ),
60            );
61          }).toList(),
62        ),
63      ),
64    );
65  }
66}

Le Fichier char_bar.dart

 1/// ./widgets/char_bar.dart
 2
 3import 'package:flutter/material.dart';
 4
 5class ChartBar extends StatelessWidget {
 6  final String label;
 7  final double spendingAmount;
 8  final double spendingPercentageOfTotal;
 9
10  ChartBar(this.label, this.spendingAmount, this.spendingPercentageOfTotal);
11
12  @override
13  Widget build(BuildContext context) {
14    return LayoutBuilder(builder: (ctx, constraints) {
15      return Column(
16        children: <Widget>[
17          Container(
18            height: constraints.maxHeight * 0.15,
19            child: FittedBox(
20              child: Text('\$${spendingAmount.toStringAsFixed(0)}'),
21            ),
22          ),
23          SizedBox(
24            height: constraints.maxHeight * 0.05,
25          ),
26          Container(
27            height: constraints.maxHeight * 0.6,
28            width: 10,
29            child: Stack(
30              children: <Widget>[
31                Container(
32                  decoration: BoxDecoration(
33                    border: Border.all(color: Colors.grey, width: 1.0),
34                    color: Color.fromRGBO(220, 220, 220, 1),
35                    borderRadius: BorderRadius.circular(10),
36                  ),
37                ),
38                Container(
39                  alignment: Alignment.bottomLeft,
40                  child: FractionallySizedBox(
41                    heightFactor: spendingPercentageOfTotal,
42                    child: Container(
43                      decoration: BoxDecoration(
44                        color: Theme.of(context).primaryColor,
45                        borderRadius: BorderRadius.circular(10),
46                      ),
47                    ),
48                  ),
49                ),
50              ],
51            ),
52          ),
53          SizedBox(
54            height: constraints.maxHeight * 0.05,
55          ),
56          Container(
57            height: constraints.maxHeight * 0.15,
58            child: FittedBox(
59              child: Text(label),
60            ),
61          ),
62        ],
63      );
64    });
65  }
66}