Flutter入门指南:从零开始学习Flutter开发

发表时间: 2024-08-15 11:57


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里的placeholderTextField( 		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.

  1. 可以在Widget外包裹一层SingleChildScrollView,这样超出的部分可以滚动
  2. 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()提前