Contents

Flutter Counter

如何写一个 Flutter 应用

我们从一个计数器讲起, 首先建立 Counter

class Counter extends StatelessWidget {
  Counter(): super();
  @override
  Widget build(BuildContext context) {
	  // TODO: implement build
	  return MaterialApp(
	    title: 'Flutter Counter',
	    theme: ThemeData(primarySwatch: Colors.blue),
	    home: CounterHomePage(title: 'Flutter Counter Home Page'),
	  );
  }
}

在内部定义了构造方法,并且重构了 build 方法 其中 build 方法返回了一个 MaterialApp ,这就是 Flutter 提供的根组件,接下来只需提供一个 Widget ,将其赋值给 home 作为主页程序即可 再来看看赋值 home 的组件 CounterHomePage 是怎么写的

class CounterHomePage extends StatefulWidget {
  CounterHomePage({this.title = 'Hello World'}): super();
  String title;

  @override
  CounterHomePageState createState() => CounterHomePageState();
}

这个类继承自 StatefulWidget ,与 Counter 类继承的 StatelessWidget 成对比, Counter 是无状态的组件, CounterHomePage 是有状态的组件 其中重写了 StatefulWidgetcreateState 方法,返回值是用来管理其状态的 CounterHomePageState

class CounterHomePageState extends State<CounterHomePage> {
  int count = 0;

  void incrementCount() {
    setState(() {
	      count += 1;
    });
  }

  @override
  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(count.toString(), style: Theme
		    .of(context)
		    .textTheme
		    .headline4),
		  TextButton(
		    child: Text('Open new route'),
		    onPressed: () {
			    Navigator.push(context, MaterialPageRoute(builder: (context){
				  return NewRoute();
			    }));
		  },)
		]
	      ),
      ),
      floatingActionButton: FloatingActionButton(
	      onPressed: incrementCount,
	      tooltip: 'Increment',
	      child: Icon(Icons.add),
      ),
    );
  }
}

其中该类继承自泛型类 State<CounterHomePage> ,与 CounterHomePage 相关联 需要改变状态的时候,调用 setState 即可,该函数接收一个闭包。 而后, Flutter 框架检测到状态改变,销毁原来 buildCouterHomePageState ,而是重新调用 build 来代替 在程序中,改变状态通过 TextButtonOnPressed 回调来触发

还有一个程序的入口,主函数没有写

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

至此,我们的第一个计数器应用就搭建完毕了

什么是 Scaffold 组件

在上面的 CounterHomePageState 中,还有个 Scaffold 组件没有讲,这是 Flutter 为我们提供的主页, 官网的描述:Material Design布局结构的基本实现, 类提供了用于显示drawer、snackbar和底部sheet的API。 可以编辑他的 appBar , body 属性,其中 body 作为主页的主体,随便放个组件进去就行了

如何布局多个组件

Scaffoldbody 只能传递一个组件,想把一堆东西塞进去,可以使用 Flutter 提供的 布局组件 该类组件都有一个 children 属性,表示一个 Widget 数组 在上面的例子中, Column 就是一个布局组件,按竖直方向布局组件,并赋值 mainAxisalignmentcenter 表示主轴方向垂直对齐


等等, Column 上面还有个 Center 组件是怎么回事? 他为什么用 child ,而不用 children ? Center 是一个容器组件,只有一个子元素,用 child 表示

布局类Widget是按照一定的排列方式来对其子Widget进行排列; 而容器类Widget一般只是包装其子Widget,对其添加一些修饰(补白或背景色等)、变换(旋转或剪裁等)、或限制(大小等)。 注意,Flutter官方并没有对Widget进行官方分类,我们对其分类主要是为了方便讨论和对Widget功能区分的记忆

除了 Center 外,其他容器类还有

  1. 填充(Padding)
  2. 尺寸限制类容器(ConstrainedBox等)
  3. 装饰容器(DecoratedBox)
  4. 变换(Transform)
  5. Container容器
  6. Scaffold、TabBar、底部导航
  7. 剪裁(Clip)

如何修饰样式

css 样式一样, flutter 中样式也可以继承,如果要更改,用 Theme 组件包装一下就可以了 其中 Theme 组件

Theme({Key? key, required ThemeData data, required Widget child})

最重要的是 ThemeData 这个结构, child 表示需要修饰的组件

对比 html 语法

对比 html 的标签语法,比如

<div>

</div>

<style>
  div {
  padding: 12px;
  }
</style>

可以用 Padding 组件写为

Padding(
  padding: EdgeInsets.all(12)
);

感觉是把一些属性单独提取出来,做成一个组件,如果需要修饰大量样式,应该用 Theme 组件