- 发表于
Flutter 整理
- Authors
- 作者
- Masachi Zhang
- @MasachiZhang
Flutter
1. 布局
Flutter的布局 与 前端的 Flex 差不多,同样是分为Row 与 Column。与前端Flex布局一样,将所有的布局样式拆成最小不可分隔的Row 或者Column,然后根据Row或者Column去做布局,如果说要加Padding,文档上写的是在需要加Padding或者Margin元素外套一层Container,类似前端的div,在div层做Padding Margin等操作
2. Stateful & Stateless
有些 widgets 是有状态的, 有些是无状态的。如果用户与 widget 交互,widget 会发生变化,那么它就是 有状态的。
无状态的 widget 自身无法改变。 Icon、IconButton 和 Text 都是无状态 widget,它们都是 StatelessWidget 的子类。
而 有状态的 widget 自身是可动态改变的(基于State)。例如,可以通过与用户的交互或是随着数据的改变而导致外观形态的变化。 Checkbox、Radio、Slider、 InkWell、Form 和 TextField 都是有状态 widget,它们都是 StatefulWidget 的子类。
一个 widget 的状态保存在一个 State 对象中,它和 widget 的显示分离。 Widget 的状态是一些可以更改的值,如一个滑动条的当前值或一个复选框是否被选中。当 widget 状态改变时,State 对象调用 setState(),告诉框架去重绘 widget。
说到底这个和React中的state的理念一样,同样也是通过state的变化来控制页面显示的元素或者元素的状态,这里的所说的元素就是Widget。
但是在Flutter里面创建一个包含State的Widget需要先创建StatefulWidget的子类 然后重写其createState方法 返回一个带State的Widget, 但是Dart里面没有像Java一样的匿名内部类,所以只能创建一个State<SubClass of StatefulWidget>
的子类来build一个真正的Widget。
与React一样,Flutter也支持自己管理或者说类似的父组件管理或者说是Redux这样的库来管理State。
我不认为完全使用Redux或者其他类似的状态管理的东西 来全局做管理。
这个在React下试过会形成一个极度糟糕的情况,任意组件触发更新就要从顶层一路下来做刷新。我个人倾向于每个组件自己管理自己的State,而公用的State或者是一些变量可存放到类似globalData中
当我们就 React 的 setState 和 Redux 的 Store 哪个好这个问题问 Redux 的作者 Dan Abramov 时, 他如此回答:
“经验原则是: 选择能够减少麻烦的方式”
总之,在任何 Flutter 应用中都存在两种概念类型的状态,短时状态经常被用于一个单独 widget 的本地状态,通常使用 State 和 setState() 来实现。其他的是你的应用应用状态,在任何一个 Flutter 应用中这两种状态都有自己的位置。如何划分这两种状态取决于你的偏好以及应用的复杂度。
3.网络请求
Flutter中网络请求可以用http或者是retrofit
关于Dart中的Retrofit 虽然说是异步获取数据,支持async 与 await,返回值与Promise类似的是一个Future对象,也可使用then方法来获取数据,当出现错误的时候通过catchError来捕获错误
ApiClient.dart
@RestApi(baseUrl: "http://www.json-generator.com/api/json/get/cffBLmFKeW?")
abstract class RestClient {
factory RestClient(Dio dio) = _RestClient;
@POST("/indent={num}")
Future<Post> getTasks(int num);
}
@JsonSerializable()
class Post{
String name;
int age;
Post({this.name, this.age});
factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
Map<String, dynamic> toJson() => _$PostToJson(this);
}
页面
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool pressed = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Retrofit Post Call"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(child: Text("Fetch Post"),
onPressed: () => {setState(() {
pressed = true;
//fetchData(postNum);
})}),
Padding(padding: EdgeInsets.all(30)),
pressed ? _buildBody(context): SizedBox(),
],
),
),
);
}
}
FutureBuilder<Post> _buildBody(BuildContext context) {
final client = RestClient(Dio(BaseOptions(contentType: "application/json")));
return FutureBuilder<Post>(
future: client.getTasks(2),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final Post posts = snapshot.data;
return _buildPosts(context, posts);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
Widget _buildPosts(BuildContext context, Post posts) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Name : "+posts.name,style: TextStyle(fontSize: 30),),
Text("Age : "+posts.age.toString(),style: TextStyle(fontSize: 30),),
],
),
);
}
这里的页面用FutureBuilder来异步获取真实数据,当snapshot.connectionState == ConnectionState.done 或者 snapshot.hasData && !snapshot.data.documents.isEmpty 时,表示数据已经获取到了。当返回 snapshot.hasError 为true时 表示数据获取错误。