Fakultas Ilmu Komputer UI

cart.dart 18.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:home_industry/Component/blue_button.dart';
import 'package:home_industry/Component/custom_circular.dart';
import 'package:home_industry/Component/price_text_formatter.dart';
import 'package:home_industry/Component/product_image.dart';
import 'package:home_industry/Component/router.dart';
import 'package:home_industry/Pages/Cart/bloc/bloc.dart';
import 'package:home_industry/Pages/Cart/model/cart_item.dart';
import 'package:provider/provider.dart';

class CartView extends StatelessWidget {
  const CartView({Key key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
18
        title: const Text('Keranjang'),
19
20
21
22
23
24
25
26
27
28
        centerTitle: true,
      ),
      bottomSheet: BlocBuilder<CartBloc, CartState>(
        condition: (previousState, state) =>
            state is CartLoaded ||
            state is CartLoadedError ||
            state is InitialCartState,
        builder: (BuildContext context, CartState state) {
          if (state is CartLoaded) {
            return Container(
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
29
              decoration: const BoxDecoration(
30
                  color: Colors.black12,
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
31
                  borderRadius: BorderRadius.only(
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
                      topLeft: Radius.circular(10),
                      topRight: Radius.circular(10))),
              width: double.infinity,
              height: MediaQuery.of(context).size.height / 14,
              padding: const EdgeInsets.symmetric(horizontal: 10),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    flex: 3,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        const Text(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
47
                          'Subtotal: ',
48
49
50
51
52
53
                          style: TextStyle(
                              fontWeight: FontWeight.w600, fontSize: 15),
                        ),
                        AnimatedSwitcher(
                          duration: const Duration(milliseconds: 400),
                          child: PriceTextFormatter(
54
55
                            key: ValueKey<String>(state.subTotal),
                            price: state.subTotal,
56
                            style: TextStyle(
57
58
                                fontWeight: FontWeight.w900,
                                fontSize: 18,
59
                                color: Theme.of(context).primaryColor),
60
61
62
63
64
65
66
67
68
69
70
71
72
73
                          ),
                        )
                      ],
                    ),
                  ),
                  Expanded(
                      flex: 2,
                      child: BlueButton(
                          onPressed: state.count == 0
                              ? null
                              : () {
                                  Navigator.of(context)
                                      .pushNamed(Router.summaryPage);
                                },
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
74
                          label: 'LANJUT'))
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
                ],
              ),
            );
          }
          return const SizedBox();
        },
      ),
      body: BlocBuilder<CartBloc, CartState>(
        condition: (previousState, state) =>
            state is CartLoaded ||
            state is CartLoadedError ||
            state is InitialCartState,
        builder: (BuildContext context, CartState state) {
          if (state is InitialCartState) {
            return const Center(
              child: CustomCircularIndicator(),
            );
          } else if (state is CartLoaded) {
            if (state.count == 0) {
              return const Center(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
95
                child: Text('Keranjang kosong yuk belanja!'),
96
97
98
99
100
              );
            }
            return _KeranjangBody(items: state.cartItems);
          }
          return const Center(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
101
            child: Text('Keranjang error, mohon coba lagi'),
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
          );
        },
      ),
    );
  }
}

class _KeranjangBody extends StatelessWidget {
  final List<CartItem> items;

  const _KeranjangBody({Key key, @required this.items})
      : assert(items != null),
        super(key: key);
  @override
  Widget build(BuildContext context) {
    return AnimatedSwitcher(
      duration: const Duration(milliseconds: 420),
      child: ListView.builder(
          key: ValueKey<int>(items.length),
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
121
          itemExtent: 130,
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
          padding: EdgeInsets.only(
              left: 5,
              right: 5,
              top: 5,
              bottom: MediaQuery.of(context).size.height / 13),
          itemBuilder: (context, index) {
            return _ItemBody(key: Key(items[index].id), item: items[index]);
          },
          itemCount: items.length,
          physics: const BouncingScrollPhysics()),
    );
  }
}

class _ItemBody extends StatelessWidget {
  final CartItem item;

  const _ItemBody({Key key, @required this.item}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Card(
        child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
          AspectRatio(
            aspectRatio: 1,
            child: Hero(
                tag: item.product.id,
                child: CachedImage(url: item.product.image)),
          ),
          Expanded(
            child: Padding(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
154
              padding: const EdgeInsets.fromLTRB(20, 8, 15, 8),
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
              child: _CartDescription(cartItem: item),
            ),
          ),
        ]));
  }
}

class _CartDescription extends StatelessWidget {
  const _CartDescription({
    Key key,
    @required this.cartItem,
  }) : super(key: key);

  final CartItem cartItem;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
        Expanded(
          flex: 5,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              Text(
                '${cartItem.product.name}',
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
                style: const TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
190
              ),
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
191
192
193
194
              const Padding(padding: EdgeInsets.only(bottom: 2)),
              PriceTextFormatter(
                price: '${cartItem.product.price}',
              ),
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
195
              if (cartItem.product.preOrder) const Text('Preorder')
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
196
197
            ],
          ),
198
199
        ),
        Expanded(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
200
          flex: 4,
201
202
203
204
205
206
207
208
209
210
211
212
213
214
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.end,
            children: <Widget>[
              Flexible(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: <Widget>[
                    AnimatedSwitcher(
                      duration: const Duration(milliseconds: 350),
                      child: RichText(
                        key: ValueKey<int>(cartItem.quantity),
                        text: TextSpan(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
215
                            text: 'Jumlah: ',
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
216
                            style: const TextStyle(color: Colors.black, fontSize: 15),
217
218
219
                            children: <TextSpan>[
                              TextSpan(
                                  text: '${cartItem.quantity}',
220
221
                                  style: TextStyle(
                                      color: Theme.of(context).primaryColor,
222
223
224
225
226
227
228
229
230
231
232
                                      fontSize: 16,
                                      fontWeight: FontWeight.w600))
                            ]),
                      ),
                    ),
                    AnimatedSwitcher(
                      duration: const Duration(milliseconds: 350),
                      child: PriceTextFormatter(
                        key: ValueKey<int>(cartItem.quantity),
                        price:
                            '${double.parse(cartItem.product.price) * cartItem.quantity}',
233
                        style: TextStyle(
234
235
                          fontSize: 16,
                          fontWeight: FontWeight.w800,
236
                          color: Theme.of(context).primaryColor,
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              Flexible(
                child: RaisedButton(
                  onPressed: () {
                    showModalBottomSheet(
                        shape: const RoundedRectangleBorder(
                            borderRadius: BorderRadius.vertical(
                                top: Radius.circular(25))),
                        context: context,
                        builder: (BuildContext context) {
                          return _ModalCartItem(cartItem: cartItem);
                        });
                  },
255
                  color: Theme.of(context).primaryColor,
256
                  child: const Text(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
257
                    'Ubah',
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
                    textAlign: TextAlign.right,
                    style: TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.w700,
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
      ],
    );
  }
}

class _ModalCartItem extends StatelessWidget {
  final CartItem cartItem;
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
276
  static const int int64MaxValue = 9223372036854775807;
277
278
279
280
281
282
283
284
285
286

  const _ModalCartItem({Key key, @required this.cartItem})
      : assert(cartItem != null),
        super(key: key);
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<CounterItem>(
      create: (_) => CounterItem(
          total: cartItem.quantity,
          minimum: 0,
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
287
          maksimum: cartItem.product.preOrder
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
288
289
              ? int64MaxValue
              : cartItem.product.stock),
290
291
292
293
294
295
296
      child: Builder(
        builder: (BuildContext context) {
          return Padding(
            padding: const EdgeInsets.only(bottom: 10),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
297
                const Center(
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
                  child: Icon(Icons.arrow_drop_down),
                ),
                Expanded(
                    flex: 7, child: CachedImage(url: cartItem.product.image)),
                Expanded(
                  flex: 9,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 10),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Flexible(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: <Widget>[
                              Text(
                                cartItem.product.name,
                                maxLines: 2,
                                overflow: TextOverflow.ellipsis,
                                style: const TextStyle(
                                    fontSize: 22, fontWeight: FontWeight.w800),
                              ),
                              Row(
                                mainAxisAlignment:
                                    MainAxisAlignment.spaceBetween,
                                children: <Widget>[
                                  Expanded(
                                    flex: 6,
                                    child: Column(
                                      crossAxisAlignment:
                                          CrossAxisAlignment.start,
                                      children: <Widget>[
                                        PriceTextFormatter(
                                          price: cartItem.product.price,
335
336
337
                                          style: TextStyle(
                                              color: Theme.of(context)
                                                  .primaryColor,
338
339
340
                                              fontSize: 20),
                                        ),
                                        Text(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
341
                                          cartItem.product.preOrder
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
342
                                              ? 'Preorder'
343
344
                                              : 'Stok: '
                                                  '${cartItem.product.stock}',
345
346
347
348
349
350
351
352
353
                                          style: const TextStyle(fontSize: 17),
                                        )
                                      ],
                                    ),
                                  ),
                                  Expanded(
                                    flex: 5,
                                    child: Row(
                                      mainAxisAlignment:
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
354
                                      MainAxisAlignment.spaceAround,
355
356
                                      children: <Widget>[
                                        Ink(
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
357
358
                                            width: 30,
                                            height: 30,
359
360
361
362
363
                                            decoration: const ShapeDecoration(
                                              color: Colors.red,
                                              shape: CircleBorder(),
                                            ),
                                            child: IconButton(
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
364
                                                icon: const Icon(Icons.remove),
365
                                                onPressed: () {
366
367
                                                  context
                                                      .read<CounterItem>()
368
369
370
                                                      .decrement();
                                                },
                                                color: Colors.white,
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
371
                                                iconSize: 15)),
372
373
374
375
376
377
378
379
                                        Consumer<CounterItem>(
                                          builder: (_, counter, __) => Text(
                                            '${counter.total}',
                                            style:
                                                const TextStyle(fontSize: 22),
                                          ),
                                        ),
                                        Ink(
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
380
381
                                            width: 30,
                                            height: 30,
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
382
                                            decoration: const ShapeDecoration(
383
                                              color: Colors.green,
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
384
                                              shape: CircleBorder(),
385
386
                                            ),
                                            child: IconButton(
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
387
                                              icon: const Icon(Icons.add),
388
                                              onPressed: () {
389
390
                                                context
                                                    .read<CounterItem>()
391
392
393
                                                    .increment();
                                              },
                                              color: Colors.white,
Tsamara Esperanti Erwin's avatar
Tsamara Esperanti Erwin committed
394
                                              iconSize: 15,
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
                                            )),
                                      ],
                                    ),
                                  )
                                ],
                              )
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 5),
                  child: ButtonTheme(
                    minWidth: double.infinity,
                    height: 45,
                    child: Consumer<CounterItem>(
                      builder: (_, counter, __) => AnimatedSwitcher(
                        duration: const Duration(milliseconds: 300),
                        child: counter.total == 0
                            ? OutlineButton(
                                onPressed: () {
                                  updateCart(context, counter);
                                },
                                child: const Text(
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
422
                                  'HAPUS',
423
424
425
426
427
428
429
430
431
432
                                  style: TextStyle(color: Colors.red),
                                ),
                                borderSide: const BorderSide(
                                    color: Colors.red, width: 2),
                                shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(10)))
                            : BlueButton(
                                onPressed: () {
                                  updateCart(context, counter);
                                },
Michael Wiryadinata Halim's avatar
Michael Wiryadinata Halim committed
433
                                label: 'UBAH'),
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
                      ),
                    ),
                  ),
                )
              ],
            ),
          );
        },
      ),
    );
  }

  void updateCart(BuildContext context, CounterItem counter) {
    BlocProvider.of<CartBloc>(context)
        .add(UpdateCartItem(counter.total, cartItem.product.id));
    Navigator.pop(context);
  }
}

class CounterItem extends ChangeNotifier {
  int _total;
  final int _minimum;
  final int _maksimum;
  CounterItem({@required total, @required minimum, @required maksimum})
      : _total = total,
        _maksimum = maksimum,
        _minimum = minimum;

  int get total => _total > _maksimum ? _maksimum : _total;

  void increment() {
    if (_total + 1 > _maksimum) {
      _total = _total;
    } else {
      _total = _total + 1;
      notifyListeners();
    }
  }

  void decrement() {
    if (_total - 1 < _minimum) {
      _total = _total;
    } else {
      _total = _total - 1;
      notifyListeners();
    }
  }
}