Nếu bạn chưa xem video "Widget of the Week" về AnimatedContainer Widget
, hãy xem video đó trước.
Bài viết này sẽ sử dụng các khái niệm được nói đến trong video và biến chúng thành code mà bạn có thể tự xử lý. Cách học nhanh nhất là vừa học vừa làm. Vì vậy, hãy mở IDE yêu thích của mình hoặc sử dụng DartPad và bắt đầu "hacking" nhé. Bạn còn chờ gì nữa mà không bắt đầu ngay bây giờ?
1. Thiết lập:
Code cho hướng dẫn này nằm trên DartPad, vì vậy hãy nhấp vào liên kết đó để bắt đầu.
Nếu bạn thích sử dụng Android Studio hoặc VS Code, hãy bắt đầu dự án Flutter mới và thay thế main.dart bằng đoạn code sau:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: AnimatedContainerDemo(),
),
);
}
}
class AnimatedContainerDemo extends StatefulWidget {
@override
_AnimatedContainerDemoState createState() => _AnimatedContainerDemoState();
}
class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
// state variables <-- state
final _myDuration = Duration(seconds: 1);
var _myValue = Color(0xFF00BB00);
final _myNewValue = Color(0xFF0000FF);
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Center(
child:
// Update this code <-- AnimatedContainer
AnimatedContainer(
color: _myValue,
duration: _myDuration,
child: SomeOtherWidget(),
),
),
updateStateButton()
],
);
}
Align updateStateButton() {
return Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsets.only(bottom: 100),
child: RaisedButton(
child: Text('Update State'),
onPressed: () {
setState(() { // <-- update state
_myValue = _myNewValue;
});
},
),
),
);
}
}
class SomeOtherWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
);
}
}
Những điều cần lưu ý:
- Các biến state được sử dụng ở trong ví dụ này được khai báo ở phía trên cùng của class State. Bạn có thể khởi tạo giá trị ban đầu và giá trị cuối cùng ở đây. Trong ví dụ đầu tiên, bạn sẽ tạo animation cho màu sắc.
- Hãy tìm widget
AnimatedContainer
ở phần giữa của đoạn code. Chúng ta sẽ cùng nhau tìm hiểu các thuộc tính khác trong bài viết này. - Ở phần dưới, bạn sẽ tìm thấy phương thức
setState()
. Bạn sẽ không cần thay đổi bất kỳ điều gì vì mọi ví dụ sẽ cập nhật_value
với_newValue
, nhưng đây là nơi bạn sẽ khiến các Animation bắt đầu animate.
2. Màu sắc
Bạn có thể tạo animation để chuyển đổi background cho Widget bất kỳ giữa hai màu khác nhau.
// start with green
var _myValue = Color(0xFF00BB00);
// end with blue
final _myNewValue = Color(0xFF0000FF);
AnimatedContainer
trông như thế này:
AnimatedContainer(
color: _myValue,
duration: _myDuration,
child: SomeOtherWidget(),
),
Trong đó _myDuration
là Duration
được set một giây.
Nếu bạn chạy ứng dụng và nhấn nút Update State ngay bây giờ, bạn sẽ thấy màu sắc thay đổi:
Ghi chú:
SomeOtherWidget
cần phải trong suốt nếu không bạn sẽ không thấy màu sắc thay đổi trênAnimatedContainer
bên dưới nó.
Nếu bạn làm việc trong Android Studio hoặc VS Code, hãy nhớ thực hiện Hot Restart thay vì Hot Reload để đặt lại (reset) state giữa mỗi ví dụ dưới đây.
3. Border
Bạn có thể tạo animation cho chiều rộng (width) của border.
// starting width
var _myValue = 0.0;
// ending width
final _myNewValue = 7.0;
AnimatedContainer
trông như thế này:
AnimatedContainer(
duration: _myDuration,
decoration: BoxDecoration(
color: Color(0xFF0099EE),
border: Border.all(
color: Color(0xFF12569A),
width: _myValue,
),
),
child: SomeOtherWidget(),
),
Nhấn nút Update State và xem sự thay đổi chiều rộng border :
3. Border Radius
Bạn có thể tạo animation để thay đổi giá trị borderRadius
của 1 Widget bất kỳ.
// starting radius
var _myValue = 0.0;
// ending radius
final _myNewValue = 40.0;
AnimatedContainer
trông như thế này:
AnimatedContainer(
duration: _myDuration,
decoration: BoxDecoration(
color: Color(0xFF0099EE),
borderRadius: BorderRadius.circular(_myValue),
border: Border.all(
color: Color(0xFF12569A),
width: 7.0,
),
),
child: SomeOtherWidget(),
),
Xem thử điều gì sẽ xảy ra khi bạn nhấn nút.
4. Background Image
Được rồi, tôi không thể tìm ra cách tạo hoạt ảnh cho hình ảnh, nhưng nếu bạn muốn thay đổi hình nền (background image), bạn có thể làm như sau:
AnimatedContainer(
duration: _myDuration,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://placebear.com/300/300'),
),
borderRadius: BorderRadius.circular(40),
),
child: SomeOtherWidget(),
),
5. Shadow
Có một số thứ có thể tạo hiệu ứng đổ bóng: màu sắc, offset, bán kính mờ (blur radius) và bán kính lan rộng (spread radius). Bán kính mờ càng lớn thì càng mờ. Bán kính lan rộng càng lớn thì bóng đổ càng lớn.
Hãy tạo hiệu ứng cho offset, bán kính mờ và bán kính lan rộng cùng một lúc.
// starting
var _myValue = 0.0;
// ending
final _myNewValue = 40.0;
AnimatedContainer
trông như thế này:
AnimatedContainer(
duration: _myDuration,
decoration: BoxDecoration(
color: Color(0xFF0099EE),
boxShadow: [
BoxShadow(
color: Colors.black,
offset: Offset(_myValue, _myValue),
blurRadius: _myValue,
spreadRadius: _myValue,
)
],
),
child: SomeOtherWidget(),
),
Nhấp vào nút Update Statei và xem độ bóng lớn dần.
6. Gradient
Bạn có thể tạo hoạt ảnh để thay đổi màu sắc gradient.
// starting
var _myValue = Colors.green;
// ending
final _myNewValue = Colors.blue;
Vì ví dụ chỉ có một biến giá trị, tôi đã thực hiện thủ thuật nhỏ phía bên phải của gradient để có thêm hoạt ảnh màu.
AnimatedContainer(
duration: _myDuration,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
_myValue,
Colors.black.withBlue(_myValue.blue),
]
),
),
child: SomeOtherWidget(),
),
Nhấn nút để xem các sắc thái thay đổi.
7. Shape
Decoration có thuộc tính shape
, nhưng bạn không thể tạo hoạt ảnh cho thuộc tính đó vì BoxShape
chỉ có thể là circle
hoặc rectangle
. Vì vậy, tôi chỉ nói về width
và height
hoạt ảnh ở đây.
// starting
var _myValue = 0.0;
// ending
final _myNewValue = 100.0;
AnimatedContainer
trông như thế này:
AnimatedContainer(
duration: _myDuration,
color: Color(0xFF0099EE),
width: 200 + _myValue,
height: 200 - _myValue,
child: SomeOtherWidget(),
),
8. Padding
Tôi phải thay đổi cấu trúc dự án để nhìn thấy sự thay đổi của padding, vì vậy tôi bỏ qua cái này. Dưới đây là đoạn code cơ bản:
AnimatedContainer(
duration: _myDuration,
padding: EdgeInsets.all(_myValue),
...
),
9. Alignment
Alignment của (0, 0)
cũng giống như căn giữa, alignment của (-1, -1)
là phía trên bên trái và (1, 1)
là phía dưới bên phải.
// starting
var _myValue = -1.0;
// ending
final _myNewValue = 1.0;
Đây là AnimatedContainer
:
AnimatedContainer(
duration: _myDuration,
alignment: Alignment(_myValue, _myValue),
color: Color(0xFF0099EE),
width: 200,
height: 200,
child: Text('Flutter'),
),
Và nó như thế này:
10. Transform
Bạn có thể xoay, dịch chuyển và nghiêng. Vấn đề là bạn phải sử dụng thêm Matrix4
để hỗ trợ làm điều đó. Đây là một đoạn code về phần đó.
// starting
var _myValue = 0.0;
// ending
final _myNewValue = 2.0;
Và AnimatedContainer
:
AnimatedContainer(
duration: _myDuration,
width: 50,
height: 50,
transform: Matrix4.skew(_myValue, _myValue),
color: Color(0xFF0099EE),
child: SomeOtherWidget(),
),
11. Duration
Chúng ta đang sử dụng duration một giây cho tất cả các ví dụ ở trên, nhưng bạn có thể thay đổi duration đó theo ý của mình.
Quay lại ví dụ về alignment, hãy làm nó chậm lại tí:
final _myDuration = Duration(days: 356);
12. Animation Curve
Animation curve cho biết tốc độ di chuyển của nó tại các điểm khác nhau trong suốt duration. Có rất nhiều loại để bạn lựa chọn và đây là ví dụ Curves.elasticInOut
về alignment (với duration được set thành một số hợp lý hơn):
AnimatedContainer(
duration: Duration(seconds: 3),
curve: Curves.elasticInOut,
alignment: Alignment(_myValue, _myValue),
color: Color(0xFF0099EE),
width: 200,
height: 200,
child: Text('Flutter'),
),
Tóm lại
Có nhiều sự kết hợp của những thứ bạn có thể tạo hoạt ảnh trên AnimatedContainer
ngay cả khi chúng ta làm việc này liên tục trong Duration(days: 356)
. Tôi chỉ cho bạn thấy những cái chính với một biến duy nhất. Bạn có thể kết hợp tùy thích với nhau.
Lưu ý nhỏ là trong khi ngẫm nghĩ viết bài này, tôi đã phát hiện ra lỗi trong cách diễn đạt thông báo lỗi Flutter.
BoxDecoration
có thuộc tính là color
, không phải là backgroundColor
. Vì vậy, tôi đã thực hiện tìm kiếm trong kho lưu trữ Flutter cho thông báo lỗi đó, hóa ra là từ một câu lệnh assert
. Sau đó, tôi đã chỉnh sửa từ ngữ trong tệp đó và thực hiện một pull request. Ngay sau đó, yêu cầu đã được chấp nhận và merged. Tôi thích Flutter vì nó là open source.
Bài viết được dịch từ đây.
Kieu Hoa
Khi mình yêu cuộc đời, cuộc đời cũng sẽ yêu mình đắm say