代码的目录结构
以新建的一个示例工程为例,主要关注下图红色标记的部分:
文件夹 | 作用 |
---|---|
android | android平台相关代码 |
ios | ios平台相关代码 |
lib | flutter相关代码,我们主要编写的代码就在这个文件夹 |
test | 用于存放测试代码 |
pubspec.yaml | 配置文件,一般存放一些第三方的依赖。 |
lib文件夹中只有一个main.dart文件,为项目的主启动程序,里面包含了类似C++的main函数:
void main() {
runApp(const MyApp());
}
自动生成的工程中,MyApp集成自StatelessWidget,为一个无状态的widget。该类返回一个MaterialApp(可以简单理解为andorid风格的ui)。在这个类中可以设置主页,以及添加自己的widget。
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
return MaterialApp(
title: 'Flutter layout demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter layout demo'),
),
body: ListView(
children: [
Image.asset(
'images/lake.jpg',
width: 600,
height: 240,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection,
],
),
),
);
上述代码中, home标识当前显示的主页,当前主页默认为Scaffold。Scaffold定义了一个 UI 框架,这个框架包含头部导航栏,body,右下角浮动按钮,底部导航栏等。所以我们借助Scaffold框架,我们可以很容易的创建一个AppBar,以及在body中创建我们所需的widget。
一个修改后的代码示例如下:
import 'package:flutter/material.dart';
// Uncomment lines 7 and 10 to view the visual layout at runtime.
// import 'package:flutter/rendering.dart' show debugPaintSizeEnabled;
void main() {
// debugPaintSizeEnabled = true;
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
Widget titleSection = const TitleWidget(title:'Oeschinen Lake Campground',desc:'Kandersteg, Switzerland');
Color color = Theme.of(context).primaryColor;
Widget buttonSection = Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildButtonColumn(color, Icons.call, 'CALL'),
_buildButtonColumn(color, Icons.near_me, 'ROUTE'),
_buildButtonColumn(color, Icons.share, 'SHARE'),
],
);
Widget textSection = const Padding(
padding: EdgeInsets.all(32),
child: Text(
'Lake Oeschinen lies at the foot of the Blüemlisalp in the Bernese '
'Alps. Situated 1,578 meters above sea level, it is one of the '
'larger Alpine Lakes. A gondola ride from Kandersteg, followed by a '
'half-hour walk through pastures and pine forest, leads you to the '
'lake, which warms to 20 degrees Celsius in the summer. Activities '
'enjoyed here include rowing, and riding the summer toboggan run.',
softWrap: true,
),
);
return MaterialApp(
title: 'Flutter layout demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter layout demo'),
),
body: ListView(
children: [
Image.asset(
'images/lake.jpg',
width: 600,
height: 240,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection,
],
),
),
);
}
Column _buildButtonColumn(Color color, IconData icon, String label) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color),
Container(
margin: const EdgeInsets.only(top: 8),
child: Text(
label,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: color,
),
),
),
],
);
}
}
class FavoriteWidget extends StatefulWidget {
const FavoriteWidget({Key? key}) : super(key: key);
@override
_FavoriteWidgetState createState() => _FavoriteWidgetState();
}
// #docregion _FavoriteWidgetState, _FavoriteWidgetState-fields, _FavoriteWidgetState-build
class _FavoriteWidgetState extends State<FavoriteWidget> {
// #enddocregion _FavoriteWidgetState-build
bool _isFavorited = true;
int _favoriteCount = 41;
// #enddocregion _FavoriteWidgetState-fields
// #docregion _toggleFavorite
void _toggleFavorite() {
setState(() {
if (_isFavorited) {
_favoriteCount -= 1;
_isFavorited = false;
} else {
_favoriteCount += 1;
_isFavorited = true;
}
});
}
// #enddocregion _toggleFavorite
// #docregion _FavoriteWidgetState-build
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(0),
child: IconButton(
padding: const EdgeInsets.all(0),
alignment: Alignment.centerRight,
icon: (_isFavorited
? const Icon(Icons.star)
: const Icon(Icons.star_border)),
color: Colors.red[500],
onPressed: _toggleFavorite,
),
),
SizedBox(
width: 18,
child: SizedBox(
child: Text('$_favoriteCount'),
),
),
],
);
}
// #docregion _FavoriteWidgetState-fields
}
class TitleWidget extends StatelessWidget {
const TitleWidget({
Key? key,
@required this.title,
@required this.desc,
}) : super(key: key);
final String? title;
final String? desc;
/*
The error you get came from null-safety, the type String? means that it could be either a String, or null, but your variable only accepts a String, and no null.
String? a;
String x = a ?? "default value";
*/
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(32),
child: Row(
children: [
Expanded(
/*1*/
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/*2*/
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
title ?? '',//'Oeschinen Lake Campground',
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Text(
desc ?? '',//'Kandersteg, Switzerland',
style: TextStyle(
color: Colors.grey[500],
),
),
],
),
),
const FavoriteWidget(),
],
),
);
}
}