
Tạo Ứng Dụng iOS Và Android Với Flutter (Phần 1)
Bước đầu khi bắt tay vào lập trình ứng dụng Mobile thì quan trọng nhất là giao diện người dùng. Giao diện phải đẹp, tối ưu thì người dùng sẽ yêu thích và sử dụng ứng dụng của bạn.
Widget là gì?
Widget là một khái niệm khá mới mẻ về Widget. Widget chính là nền tảng của Flutter, một Widget miêu tả một phần của giao diện người dung. Tất cả các component như text, image, button hay animation, … cũng là 1 Widget. Trong Flutter, tất cả các Widget hay giao diện đều được code bằng ngôn ngữ Dart.
- Khi một widget thay đổi trạng thái, chẳng hạn như do người dung click hay animation, widget sẽ tự xây dựng lại theo trạng thái mới.
Hello World
import 'package:flutter/material.dart';
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
- Hàm runApp() chấp nhận 1 widget như 1 tham số truyền vào, nó là gốc của 1 hệ thống cây widget. Trong ví dụ trên có 2 widget được sử dụng là Center và Text. Mỗi chương trình flutter được chạy và deloy lên mobile app (android hoặc ios) đầu tiên nó sẽ chạy vào hàm runApp(). Widget gốc sẽ chịu trách nhiệm hiển thị dòng chữ Hello, world ở giữa màn hình hiển thị vì sử dụng Center.
Basic Widget
Flutter đi kèm với một bộ các Widget cơ bản mạnh mẽ, trong đó thường sử dụng :
- Text : cho phép bạn tạo văn bản trong ứng dụng của mình
- Row, Column : cho phép bạn tạo bố cục linh hoạt theo cả 2 chiều horizontal(row) – ngang và vertical(column) – dọc. Thiết kế này dựa trên mô hình flexbox của web.
- Stack : thay vì định hướng tuyến tính theo chiều ngang hoặc chiều dọc, stack cho phép bạn đặt các widget chồng lên nhau theo thứ tự. Bạn có thể sử dụng widget Positioned là con của widget Stack để định vị chúng so với left, right, bottom, top. Stack thiết kế dựa trên mô hình absolute position web.
- Container : cho phép bạn tạo ra 1 thành phần hình chữ nhật chứa trong layout. Nghĩa là tạo ra 1 thùng chứa.
- Dưới đây là ví dụ về việc sử dụng các widget cơ bản :
import 'package:flutter/material.dart';
class MyAppBar extends StatelessWidget {
MyAppBar({this.title});
// Fields in a Widget subclass are always marked "final".
final Widget title;
@override
Widget build(BuildContext context) {
return Container(
height: 56.0, // in logical pixels
padding: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(color: Colors.blue[500]),
// Row is a horizontal, linear layout.
child: Row(
// <Widget> is the type of items in the list.
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null, // null disables the button
),
// Expanded expands its child to fill the available space.
Expanded(
child: title,
),
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
);
}
}
class MyScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Material is a conceptual piece of paper on which the UI appears.
return Material(
// Column is a vertical, linear layout.
child: Column(
children: <Widget>[
MyAppBar(
title: Text(
'Example title',
style: Theme.of(context).primaryTextTheme.headline6,
),
),
Expanded(
child: Center(
child: Text('Hello, world!'),
),
),
],
),
);
}
}
void main() {
runApp(MaterialApp(
title: 'My app', // used by the OS task switcher
home: MyScaffold(),
));
}
Sử dụng Material Components
- Flutter cung cấp cho bạn một số widget, giúp bạn xây dựng ứng dụng tuân theo 1 tiêu chuẩn thiết kế và ở đây là Material Design.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter Tutorial',
home: TutorialHome(),
));
}
class TutorialHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null,
),
title: Text('Example title'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
// body is the majority of the screen.
body: Center(
child: Text('Hello, world!'),
),
floatingActionButton: FloatingActionButton(
tooltip: 'Add', // used by assistive technologies
child: Icon(Icons.add),
onPressed: null,
),
);
}
}
Handling gestures ( xử lý cử chỉ, thao tác )
- Hầu hết các ứng dụng bao gồm 1 vài form tương tác người dung với hệ thống. Bước đầu tiên trong việc xây dựng một ứng dung tương tác là phát hiện các cử chỉ đầu vào ( detect input gestures).
class MyButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
print('MyButton was tapped!');
},
child: Container(
height: 36.0,
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Colors.lightGreen[500],
),
child: Center(
child: Text('Engage'),
),
),
);
}
}
- Widget GestureDetector không có biểu hiện trực quan mà thay vào đó phát hiện các cử chỉ do người dung thực hiện. Khi người dung chạm vào Container, GestureDetector sẽ gọi lại hàm onTap() và hiển thị ra 1 thông báo trong màn hình console.
- Nhiều widget sử dụng GestureDetector để cung cấp các cuộc gọi lại (callback) tùy chọn cho các widget khác nhau. Ví dụ như : IconButton, RaisedButton, FloatingActionButton, … có hàm onPress() được kích hoạt khi người dung chạm vào widget.
Changing widgets in response to input ( Thay đổi widget đáp ứng đầu vào )
- Stateless Widgets – widget phi trạng thái nhận các đối số từ widget cha của chúng, chúng lưu trữ trong các biến final. Khi một widget được build() nó sử dụng các giá trị được lưu trữ này trở thành các đối số mới cho widget mà nó tạo ra.
- Statefull Widgets – widget có trạng thái (State). State ở đây là trạng thái của widget chứ không phải là trạng thái của biến. Ví dụ như khi bạn click vào button biến đếm tang dần như sau :
class Counter extends StatefulWidget {
// This class is the configuration for the state. It holds the
// values (in this case nothing) provided by the parent and used
// by the build method of the State. Fields in a Widget
// subclass are always marked "final".
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
// This call to setState tells the Flutter framework that
// something has changed in this State, which causes it to rerun
// the build method below so that the display can reflect the
// updated values. If you change _counter without calling
// setState(), then the build method won't be called again,
// and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called,
// for instance, as done by the _increment method above.
// The Flutter framework has been optimized to make rerunning
// build methods fast, so that you can just rebuild anything that
// needs updating rather than having to individually change
// instances of widgets.
return Row(
children: <Widget>[
RaisedButton(
onPressed: _increment,
child: Text('Increment'),
),
Text('Count: $_counter'),
],
);
}
}
- Trong Flutter Statefull Widget và State là 2 đối tượng riêng biệt, chúng có vòng đời khác nhau. Các widget là các đối tượng tạm thời, được sử dụng để xây dựng một bản trình bày của ứng dụng ở trạng thái hiện tại. Mặt khác, các đối tượng State vẫn tồn tại luôn luôn lắng nghe sự thay đổi để gọi tới hàm build(), cho phép chúng ghi nhớ thông tin.
class CounterDisplay extends StatelessWidget {
CounterDisplay({this.count});
final int count;
@override
Widget build(BuildContext context) {
return Text('Count: $count');
}
}
class CounterIncrementor extends StatelessWidget {
CounterIncrementor({this.onPressed});
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: onPressed,
child: Text('Increment'),
);
}
}
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return Row(children: <Widget>[
CounterIncrementor(onPressed: _increment),
CounterDisplay(count: _counter),
]);
}
}
- Ở ví dụ trên, việc tạo 2 Stateless Widgets, phân tách rõ rang về việc hiển thị bộ đếm (CounterDisplay) và thay đổi bộ đếm (CounterIncrementor). Mặc dù kết quả sẽ giống như ví dụ sử dụng Statefull Widget nhưng việc phân tách khá phức tạp và tốn thời gian hơn.
Responding to widget lifecycle events (Đáp ứng các sự kiện vòng đời của widget)
- Khi Flutter xây dựng StatefullWidget, nó sẽ ngay lập tức gọi createState().
- Khi createState() tạo lớp trạng thái, buildContext được gán cho trạng thái đó. BuildContext có nhiệm vụ xác định và điều khiển vị trí của môt widget trong cây widget. Tất cả các widget đều có thuộc tính bool this.mounted. Nó trả về true khi buildContext được gán. Và sẽ trả về lỗi khi gọi setState() nếu 1 widget bị ngắt kết nối.
- InitState() : phương thức đầu tiên được gọi khi widget được tạo. Nó chỉ được gọi 1 lần và chỉ 1 lần.
- Sau khi gọi initState() thì phương thức didChangeDependencies() được gọi.
- Build() : phương thức này thường được gọi thương xuyên để render, trả về 1 widget.
- DidUpdateWidget() : được gọi khi widget cha thay đổi và phải xây dựng lại widget.
- SetState() : thường được gọi từ phía Flutter framework và từ phía developer. Nó được sử dụng để thông báo rằng “ dữ liệu đã được thay đổi” và widget ở build context nên được rebuilt.
- Dispose() : được gọi khi đối tượng State được xóa vĩnh viễn.
- Mount is false : khi đối tượng state không bao giờ có thể remount và khi bạn gọi setState() sẽ có lỗi.
Key
- Là một mã định danh cho Widgets, Elements and SemanticsNodes
- Một widget mới sẽ chỉ được sử dụng để cập nhật một phần tử hiện có nếu khóa của nó giống với khóa của widget hiện tại được liên kết với phần tử đó.
- Các khóa phải là duy nhất.
GlobalKey
- Khóa duy nhất trên toàn bộ ứng dụng.
- Cung cấp quyền truy cập vào các đối tượng khác được liên kết với các thành phần đó, chẳng hàn như BuildContext. Đối với StatefullWidget, globalKey cũng cung cấp quyền truy cập vào State.
Tạm kết
Trong phần này mình đã giới thiệu cơ bản về widget trong flutter để tạo ra 1 ứng dụng trên ios hay android. Các bạn cùng đón chờ phần tiếp theo nhé.
Post Comment