Youtube视频地址:
https://www.youtube.com/watch?v=OO_-MbnXQzY&t=437s
dart在线工具:
https://dartpad.dev/
Widget
Flutter是widget的嵌套
MaterialApp是应用的起点
import'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home:Material(
child: Text('Hello world'),
),
));
}
StatelessWidget内返回的必须是Widget
必须用StatelessWidget才能reload(热更新)
Column 部件用children, children是个list,因为有多个
mainAxisAlignment改变Column的沿主轴的分布方式
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('hello, welcome back!'),
Text('Login to continue'),
Text('new text'),
],
)
Scaffold部件可以新增更多的属性,方便排版布局;
用Scaffold 替代Material时,需要将child改为body
TextField是全屏的宽度,所以会影响到组件的布局
// hintText是提示词,类似web里的placeholder
TextField(
decoration: InputDecoration(hintText: 'username'),
)
按钮:
//字体按钮
TextButton(onPressed: () {
print('clicked');
}, child: Text('Forget Password?')),
//主按钮
ElevatedButton(onPressed: () {
print('Login is clicked');
}, child: Text('Log in')),
图片及字体
添加图片,在根目录下新建assets/images目录,并需要在pubspec.yaml中加入该图片文件夹的路径:
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in # the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
可以直接新增一个文件将原UI代码拆开放在一个新的class(继承自StatelessWidget),在原文件中可直接引用该类
修改Widget样式的时候,可用Wrap将Widget作为child包裹在里面
Google fonts:
https://fonts.google.com/
在assets目录下新建fonts目录,并需要在pubspec.yaml中加入字体目录,修改对应的字重及对应weight数值
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in # the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
fonts:
- family: Urbanist
fonts:
- asset: assets/fonts/Urbanist-Light.ttf
weight: 300
- asset: assets/fonts/Urbanist-Regular.ttf
- asset: assets/fonts/Urbanist-Medium.ttf
weight: 500
- asset: assets/fonts/Urbanist-SemiBold.ttf
weight: 600
- asset: assets/fonts/Urbanist-Bold.ttf
weight: 700
- asset: assets/fonts/Urbanist-ExtraBold.ttf
weight: 800
- asset: assets/fonts/Urbanist-Black.ttf
weight: 900
App Figma地址:
https://www.figma.com/file/ldUCcQApMTiTzs2jzAx7Iw/Flutter-course?type=design&node-id=0-1&mode=design&t=8JNDMsrUCxrRgQO8-0
Theme
lib文件夹下创建styles文件夹,创建app_colors.dart,加入颜色
import 'package:flutter/material.dart';
class AppColors{
static const primary = Color(0xfffbd512);
static const font = Color(0xffD8D8D8);
static const font2 = Color(0xff373737);
static const disableFont = Color(0xffa7a7a7);
static const disableButton = Color(0xff303030);
static const background = Color(0xff1A2947);
static const black = Color(0xff000000);
static const white = Color(0xffffffff);
}
在main.dart > MaterialApp中加入theme可修改主题(全局样式)
import 'package:first1/login_page.dart';
import 'package:first1/styles/app_colors.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
fontFamily: 'Urbanist',
scaffoldBackgroundColor: AppColors.background,
),
home: LoginPage(),
);
}
}
弹性布局
Spacer(),可以根据屏幕尺寸自动加一些空间,而SizedBox()是固定的尺寸,需要根据UI灵活调整用哪个Widget
渲染溢出:
A RenderFlex overflowed by xxx pixels on the bottom.
- 可以在Widget外包裹一层SingleChildScrollView,这样超出的部分可以滚动
- resizeToAvoidBottomInset: false弹出键盘后不改变布局,但会导致在键盘位置的输入框无法显示(尽量不要使用)
SingleChildScrollView与Spacer()冲突,可以在Widget之间再加个SizedBox给出明确的高度或使用height: MediaQuery.of(context).size.height查询设备高度
width: double.infinity可以填满宽度或高度
路由
方法一:MaterialPageRoute:
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return HomePage();
}
)
);
}
child: Text('Log in')
),
方法二:在MaterialApp中建立routes,并在点击时调用routes name
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
fontFamily: 'Urbanist',
scaffoldBackgroundColor: AppColors.background,
),
initialRoute: '/',
routes: {
'/': (context) => LoginPage(),
'/home': (context) => HomePage(),
},
);
}
}
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/home');
},
child: Text('Log in')
),
如果不希望返回,如登录页到首页,则用pushReplacementNamed('/home')替代pushNamed('/home')
绘制列表时,用ListView()替代Column(),性能会更好
StatefulWidget
使用setState改变状态
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
const TestPage({super.key});
@override
State<TestPage> createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text(
'This is a counter: $count',
style: TextStyle(
fontSize: 30,
color: Colors.white,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
count++;
});
},
child: Icon(Icons.add),
),
);
}
}
SVG
https://pub.dev/
使用flutter_svg第三方库
下面的记录不完整
下方导航栏问题:
type: BottomNavigationBarType.shifting,
组件复用
PreferredSizeWidget
implements ?
PopupMenuButton
ClipRRect
Expanded()等分,类似css的dsiplay:flex
contentPadding: EdgeInsets.zero,清除默认Padding
copyWith继承样式并修改属性
ColorFilter
extendBody: true, 显示组件遮挡(多余)的部分
Map
fultter_map
https://docs.fleaflet.dev/
视频中的openstreet连不上
用的ArcGIS,下面这个
https://server.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}.png
知乎:
https://zhuanlan.zhihu.com/p/641436984
GridView
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// crossAxisSpacing: 2.0,
// mainAxisSpacing: 4.0, childAspectRatio: 0.75,
mainAxisExtent: 260),
itemCount: 9, // 假设有20张图片
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ClipRRect(
clipBehavior: Clip.antiAlias,
child: Image.network(
'https://shorturl.at/csQTX',
width: double.infinity,
fit: BoxFit.cover,
)),
// 替换为你的图片部件
SizedBox(height: 8.0),
Text(
'Doutei Yuusha no...',
style:
TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold),
),
Text(
'Author',
style: TextStyle(fontSize: 14.0, color: Colors.grey),
),
SizedBox(height: 8.0),
],
),
),
);
},
)
TabBar
DefaultTabController(
length: 3, // 标签的数量
child: Scaffold(
appBar: AppBar(
title: Text('Bookshelf'),
bottom: TabBar(
tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
body: TabBarView(
children: [
TabContent('Tab 1 Content'),
TabContent('Tab 2 Content'),
TabContent('Tab 3 Content'),
],
),
),
)
文字超出显示省略号
https://blog.csdn.net/qq_39081974/article/details/100201821
Text(
'Sample Text',
softWrap:true,
textAlign:TextAlign.left,
overflow:TextOverflow.ellipsis,
maxLines:3,
style:Textstyle(
fontSize:14,
),
),
Border
https://blog.csdn.net/ww897532167/article/details/111933624
WidgetsFlutterBinding.ensureInitialized()
https://juejin.cn/post/7031196891358429220
WidgetsFlutterBinding 将是 Widget 架构和 Flutter Engine 连接的核心桥梁,也是整个 Flutter 应用层的核心。通过 ensureInitialized() 方法我们可以得到一个全局单例 WidgetsFlutterBinding。
有时候我们会在发现有的app 在在运行应用程序之前先与 Flutter Engine 进行通信,所以要先将WidgetsFlutterBinding.ensureInitialized()提前