Bài 04: Khái niệm thẻ-Widget, StatelessWidget

Bài 04: Khái niệm thẻ-Widget, StatelessWidget

  

Widgets trong Flutter

1. tại sao học widget đầu tiên

Gắn với khái niệm code ra được cái app hello world thì bạn cần biết widget là gì để có thể tạo ra các widget. Vì trong flutter, khái niệm widget là khái niệm xuyên suốt trong quá trình code với flutter. 

nếu bạn nào đã từng tiếp xúc với react, vue, … nói chung là các frameword khuynh hướng hiện đại sẽ mang tư tưởng của component. và flutter cũng thế, Flutter widgets cũng giống 1 component trong reactjs. Flutter widgets mục đích là biểu thị UI, render UI cho người dùng thấy. Flutter có cơ chế state, khi state thay đổi thì widget của flutter sẽ được render lại.

2. Chỉnh sửa lại code của project fluter cho dễ đọc.

chũng ta nên viết lại 1 cách ngắn gọn như này : 

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text("đây là title của app bar")
        ),
        body: Center(
          child: Text("đây là thẻ center thử hot reload"),
        ),
      )
    );
  }
}
Dart

bạn copy dán vào file main.dart để xem code thực hiện của flutter sẽ có kết quả như này: 

 

Tổng quanvề Widget

Không giống code native như ở Android hoặc IOS, Flutter có một cơ chế build, update UI hoàn toàn khác biệt. Và cơ chế đó như sau :

khi trạng thái ( state ) thay đổi thì UI sẽ được render lại ( function Widget build(BuildContext context) { sẽ được gọi lại. 

Tức là với ví dụ trên, thì đầu game flutter chạy hàm main.dart, vào đây thấy cái hàm void main thì chạy trước, hàm voi main thì cho chạy cái hàm runApp, mà cụ thể cái runApp, vì nó là thư viện flutter cung cấp.

Trong hàm runApp nó chạy cái widget MyApp do chính chúng ta định nghĩa. Mình thích định nghĩa cái gì cũng được. tạm thời chúng ta sẽ thấy là MyApp sẽ extend từ cái  StatelessWidget  ngoài ra chúng ta còn có 1 loại khác để có thể kế thừa là StatefulWidget 

  • StatelessWidget là không có state ( không thay đổi nội bộ UI )
  • StatefullWidget là có state ( có sự thay đổi quản lý state và sự thay đổi UI theo state)

Trong flutter họ có định nghĩa sẵn cho chúng ta vài cái widget để sài cho tiện

trong ví dụ là MaterialApp là widget kế thừa các kiến trúc theo chuẩn design marterial, Scaffold là widget bao chứa toàn bộ code chúng ta vào trong nó ( phủ đầy giao diện chúng ta).

 => Bạn nào code HTML rồi thì cứ tưởng tượng cái thẻ marterial giống như là thẻ html có thẻ head rồi nhúng css của marterial vào, còn thẻ scaffold là như thẻ body của html vậy thôi.

Trong 1 thẻ widget thì sẽ có các Thộc tính đi kèm, có thể các thuộc tính đi kèm lại truyền thêm các widget, giống như bạn xây dùng 1 thẻ Widget cho riêng bạn, tuy kế thừa từ widget nhưng cũng có thể có các thuộc tính đi kèm là đối tượng widget được truyền vào.

ví dụ trên là thẻ MaterialApp có thuộc tính title truyền vào là 1 text, và thuộc tính theme truyền vào là 1 ThemeData. Nhưng còn thẻ Scaffold thì co thuộc tính body truyền vào 1 widget Center

Widget thì được chia ra làm 2 loại. Đó là StatelessWidget và StatefulWidget. Hiện tại chúng ta cũng thấy có 2 cái widget Text và Center trong ví dụ trên, vậy câu hỏi mình tự đặt ra khi mới học flutter đó là 2 cái widget đó là dạng nào trong 2 loại widget của flutter ? Vậy câu trả lời là nếu bạn view define nó ra sẽ thấy nó extend từ đứa nào là biết chứ cần gì search chi phức tạp 

Tổng kết
Bài này dài rồi, mình xin tóm gọn là chỉ bằng 2 câu nói: 
1. Để xây dựng các thành phần UI trong fluttter chúng ta dùng các widget ( widget thì giống khái niệm component trong các kiến trúc web )
2. chúng ta có 2 loại widget làStatelessWidgetvàStatefulWidget

1) Stateless widgets

Stateless widget không có state. Nó không chấp nhận sự thay đổi bên trong nó. Còn đối với sự thay đổi từ bên ngoài (widget cha) thì nó sẽ thụ động thay đổi theo.
Có nghĩa là StatelessWidget chỉ đơn thuần nhận dữ liệu và hiển thị 1 cách thụ động. Việc tương tác với nó không sinh ra bất kỳ một event nào để chính bản thân phải render lại. Nếu phải render lại thì là do tác động từ bên ngoài vào.
Vậy nên, nó không có liên quan gì đến State cả. Bản thân nó cũng không có hàm createState mà thay vào đó là hàm build(BuildContext)
Ví dụ: ta có thể nhìn 1 vài mẫu Stateless widgets.

Xét loại Text widget là để khởi tạo trong một constructor và những properties thường để build widget và hiển thị lên màn hình

2) Stateful widgets

Nghe qua là hiểu StatefulWidget là Widget này có State. Ở đây sẽ có bạn nhầm lẫn cho rằng State là trạng thái của các biến. Nhưng thực tế State là trạng thái của cả 1 widget.
Để hiểu rõ hơn ta xét ví dụ như sau:
Khi xoay màn hình thì coi như ứng dụng sẽ dựng lại các Widget và sẽ render lại Widget từ những dữ liệu ban đầu. Trong Android SDK để khôi phục lại trạng thái cho các TextView, RadioButton, CheckBox, ... bạn sẽ phải thao tác với savedInstanceState. Với Flutter thì nó làm sẵn với tên State này, nó sẽ tận dụng lại State cũ, lấy tất cả giá trị trong đó ra để render lại Widget.
Và ví dụ cho StatefulWidget sẽ là như sau:

II. Demo

Để nhanh hiểu ta cứ làm ví dụ cho nhanh Trong bài này ta sẽ thử với 3 action sau:

  1. Làm thế nào để chuyền app state cho widget
  2. Làm sao để rebuild widget sau khi cập nhật state
  3. Làm sao có thể điều hướng màn hình và vẫn đồng bộ được state.

Cụ thể sẽ là:

  • Tăng biến đếm trong MyHomePage
  • Chuyển sang màn hình MySecondPage
  • Giảm biến đếm trong MySecondPage

Hiện tại bạn chỉ nên quan tâm file code trong lib folder thôi nhé

Trong main.dart như sau:
import 'package:flutter/material.dart';
import 'MyHomePage.dart';

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

class MyApp extends StatefulWidget {
  
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _MyAppState();
  }
}

class _MyAppState extends State<MyApp> {
  int counter;

  
  void initState() {
    // TODO: implement initState
    super.initState();
    counter = counter ?? 0;
  }

  void _decrementCounter(_){
    setState(() {
      counter--;
      print('decrement: $counter');
    });
  }

  void _incrementCounter(_){
    setState(() {
      counter++;
      print('increment: $counter');
    });
  }

  
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(
        title: 'My Home Page',
        counter: counter,
        decrementCounter: _decrementCounter,
        incrementCounter: _incrementCounter,
      ),
    );
  }
}

Trong MyHomePage.dart

import 'package:flutter/material.dart';

import 'MySecondPage.dart';

class MyHomePage extends StatefulWidget {
  MyHomePage(
      {Key key,
      this.title,
      this.counter,
      this.decrementCounter,
      this.incrementCounter})
      : super(key: key);

  final String title;
  final int counter;
  final ValueChanged<void> decrementCounter;
  final ValueChanged<void> incrementCounter;

  
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  void _onPressed() {
    widget.incrementCounter(null);
  }

  
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              widget.counter.toString(),
              style: Theme.of(context).textTheme.display1,
            ),
            RaisedButton(
              child: Text('next screen'),
              onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => MySecondPage(
                              widget.decrementCounter,
                              title: 'My Second Page',
                              counter: widget.counter,
                            )));
              },
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _onPressed,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Và cuối cùng MySecondPage.dart

import 'package:flutter/material.dart';

class MySecondPage extends StatefulWidget {
  MySecondPage(
    this.decrementCounter, {
    Key key,
    this.title,
    this.counter,
  }) : super(key: key);

  final String title;
  final int counter;
  final ValueChanged<void> decrementCounter;

  
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _MySecondPageState();
  }
}

class _MySecondPageState extends State<MySecondPage> {
  void _onPressed() {
    widget.decrementCounter(null);
  }

  
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              super.widget.counter.toString(),
              style: Theme.of(context).textTheme.display1,
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _onPressed,
        tooltip: 'Decrement',
        child: Icon(Icons.indeterminate_check_box),
        backgroundColor: Colors.red,
      ),
    );
  }
}

 

Đăng nhận xét

0 Nhận xét

myadcash