Dart Flutter 学习笔记(2020最全)

一、介绍

1、dart语言

Dart是谷歌开发的计算机编程语言,后来被Ecma (ECMA-408)认定为标准。它被用于web、服务器、移动应用和物联网等领域的开发。它是宽松开源许可证(修改的BSD证书)下的开源软件。

Dart是面向对象的、类定义的、单继承的语言。它的语法类似C语言,可以转译为JavaScript,支持接口(interfaces)、混入(mixins)、抽象类(abstract classes)、具体化泛型(reified generics)、可选类型(optional typing)和sound type system。

2、flutter框架

Flutter 由 Google 的工程师团队打造,用于创建高性能、跨平台的移动应用。Flutter 针对当下以及未来的移动设备进行优化,专注于 Android and iOS 低延迟的输入和高帧率。

Flutter 可以给开发者提供简单、高效的方式来构建和部署跨平台、高性能移动应用;给用户提供漂亮、快速、jitter-free 的 app 体验。

二、新建Flutter项目

1、Flutter目录结构
文件夹					作用
android					android平台相关代码
ios						ios平台相关代码
lib						flutter相关代码,我们主要编写的代码就在这个文件夹
test					用于存放测试代码
pubspec.yaml			配置文件,一般存放一些第三方库的依赖
2、Flutter入口文件和入口方法
void main(){
	runApp(MyApp());
}

//也可以简写
void main() => runApp(MyApp());

//其中的main方法是dart的入口方法。runApp方法是flutter的入口方法。MyApp是自定义的一个组件
3、使用MaterialApp 和 Scaffold两个组件装饰App

1)、MaterialApp

MaterialApp是一个方便的Widget,它封装了应用程序实现Material Design所需要的一些Widget。一般作为顶层widget使用。

2)、Scaffold

Scaffold是Material Design布局结构的基本实现。此类提供了用于显示drawer、snackbar和底部sheet的API。

一个flutter app的基本结构

import 'package:flutter/material.dart';


void main() {
  runApp(MyApp());
}

//自定义组件
class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {

    return MaterialApp(// 基本结构
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Demo'),
        ),
        body: HomeContent(),
      ),
      theme: ThemeData(// 主题颜色
        primarySwatch: Colors.green
      ),
    );

  }
}

三、Flutter组件

1、常用组件
1)、Center

基于父容器居中显示

2)、Text

文本组件

参数:

  const Text(
	this.data, {
	Key key,
	this.style,// 字体样式设置
	this.strutStyle,
	this.textAlign,// 文本对齐方式
	this.textDirection,// 文本方向
	this.locale,
	this.softWrap,
	this.overflow,// 文字超出屏幕之后的处理方式
	this.textScaleFactor,// 字体显示的倍率
	this.maxLines,// 字体显示的最大行数
	this.semanticsLabel,
	this.textWidthBasis,
	this.textHeightBehavior,
  })
3)、Container

容器组件

4)、Image

图片组件

加载网络图片:

Image.network("src")

实现圆形图片(ClipOval组件)

ClipOval(
	child:Image.network("src"),
	width:150.0,
	height:150.0
)

加载本地图片:

2、ListView

1)、循环实现动态列表

//首页
class HomeContent extends StatelessWidget{

  //自定义方法  私有方法
  List<Widget> _getData(){
    var list = listData.map((value){// map循环
      return ListTile(
        leading: Image.network(value["imageUrl"]),
        title:Text(value["title"]),
        subtitle: Text(value["author"]),
      );
    });

    return list.toList();
  }

  @override
  Widget build(BuildContext context) {

    return ListView(
      children: this._getData(),
    );
  }

}

2)、ListView.builder实现动态列表

//首页
class HomeContent extends StatelessWidget{

  Widget _getListData(context,index){
    return ListTile(
      title: Text(listData[index]['title']),
      subtitle: Text(listData[index]['author']),
      trailing: Image.network(listData[index]['imageUrl']),
    );
  }

  @override
  Widget build(BuildContext context) {

    return ListView.builder(
      itemCount: listData.length,
      itemBuilder: this._getListData,// 代表赋值
    );
  }

}
3、GridView

GridView.count加载动态数据

GridView.builder加载动态数据

//首页
class HomeContent extends StatelessWidget{

  Widget _getListData(context,index){
    return Container(
      child: Column(// 垂直布局
        children: <Widget>[
          Image.network(listData[index]['imageUrl']),
          SizedBox(// 通过sizedbox来划分图片和文本
            height: 20,
          ),
          Text(
            listData[index]['title'],
            textAlign: TextAlign.center,
            style: TextStyle(
                fontSize: 20
            ),
          )
        ],
      ),
      decoration: BoxDecoration(
        border: Border.all(
          color: Color.fromRGBO(233, 233, 233, 0.9),
          width: 1,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(// 配置网格属性
        crossAxisSpacing: 10,// 水平 子Widget 距离
        mainAxisSpacing: 10,// 垂直 子Widget 距离
        crossAxisCount: 2,// 控制一行widget的数量

        childAspectRatio: 0.7,// 宽度和高度的比例
      ),
      itemCount: listData.length,
      itemBuilder: this._getListData,

    );
  }

}
4、页面布局
1)、Padding

使用Padding组件处理容器与子元素直接的间距

2)、Row

行布局组件

3)、Column

列布局组件

4)、Expanded

Expanded可以用在Row和Column组件中

属性					说明
flex					元素占整个父Row/Column的比例
child					子元素
5)、Stack

Stack层叠组件(Stack与Align 、 Positioned 实现定位布局)

Align 示例:

//首页
class HomeContent extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 400,
        width: 200,
        color: Colors.red,
        child: Stack(//  使用stack 和 align 来控制子元素位置
          children: <Widget>[
            Align(
              alignment: Alignment.center,
              child: Icon(Icons.home,size: 20,color: Colors.white,),
            ),
            Align(
              alignment: Alignment.bottomCenter,
              child: Icon(Icons.search,size: 60,color: Colors.white,),
            ),
            Align(
              alignment: Alignment.centerRight,
              child: Icon(Icons.gavel,size: 40,color: Colors.white,),
            ),
          ],
        ),
      ),
    );
  }

}

Positioned示例:

//首页
class HomeContent extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 400,
        width: 200,
        color: Colors.red,
        child: Stack(//  使用stack 和 align 来控制子元素位置
          children: <Widget>[
            Positioned(
              left: 10,
              bottom: 0,
              child: Icon(Icons.home,size: 20,color: Colors.white,),
            ),
            Positioned(
              left: 10,
              bottom: 50,
              child: Icon(Icons.search,size: 60,color: Colors.white,),
            ),
            Positioned(
              right: 10,
              bottom: 0,
              child: Icon(Icons.gavel,size: 40,color: Colors.white,),
            ),
          ],
        ),
      ),
    );
  }

}
5、AspectRatio

AspectRatio的作用是根据设置调整子元素child的宽高比。

AspectRatio首先会在布局限制条件允许的范围内尽可能的扩展,widget的高度是由宽度和比率决定的,类似与BoxFit中的contain,按照固定比率去尽量占满区域。

如果在满足所有限制条件过后无法找到一个可行的尺寸,AspectRatio最终将会去优先适应布局限制条件,而忽略所设置的比率。

属性					说明
aspectRatio				宽高比
6、Card

卡片组件

//首页  listData是外部引入的数据
class HomeContent extends StatelessWidget{

  List<Widget> _getData(){

    var list = listData.map((value){

      return Card(
        margin: EdgeInsets.all(10),
        child: Column(
          children: <Widget>[
            AspectRatio(
              aspectRatio: 1.0/1.0,
              child: Image.network(value['imageUrl']),
            ),
            ListTile(
              leading: CircleAvatar(
                backgroundImage: NetworkImage(value['imageUrl']),
              ),
              title: Text(value['title'],style: TextStyle(fontSize: 16),),
              subtitle: Text(value['author']),
            )
          ],
        ),
      );
    });
    return list.toList();
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: this._getData(),
    );
  }
}

7、Wrap

Wrap可以实现流布局,单行的Wrap跟Row表现几乎一致。但Row和Column都是单行单列的,Wrap则突破了这个限制,mainAxis上空间不足时,则向crossAxis上去扩展显示。(类似与前端web的flex布局)

四、组件状态

在Flutter中自定义组件其实就是一个类,这个类需要继承StatelessWidget/StatefulWidget。

StatelessWidget是无状态组件,状态不可变的widget

StatefulWidget是有状态组件,持有的状态可能在widget生命周期改变。通俗的将:如果我们想改变页面中的数据的话这个时候就需要用到StatefulWidget

1、无状态组件

示例:

//无状态组件  无法改变页面中的数据
class HomeContent extends StatelessWidget{

  int countNum = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        SizedBox(height: 200,),
        Text('${this.countNum}'),
        RaisedButton(
          child: Text('button'),
          onPressed: (){
            this.countNum ++;
            print(this.countNum);
          },
        ),
      ],
    );
  }
}
2、有状态组件

示例:

//有状态组件  能够改变页面中的数据
class HomeContent extends StatefulWidget{

  HomeContent({Key key}) : super(key : key);

  _HomeContentState createState() => _HomeContentState();

}



class _HomeContentState extends State<HomeContent>{

  int countNum = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        SizedBox(height: 20,),
        Chip(
          label: Text('${this.countNum}'),
        ),
        RaisedButton(
          child: Text('button'),
          onPressed: (){
            setState(() {// 该方法只在有状态组件中有
              this.countNum ++;
            });
            print(this.countNum);
          },
        ),
      ],
    );
  }

}

五、BottomNavigationBar 自定义底部导航条

BottomNavigationBar是底部导航条,可以让我们定义底部Tab切换,BottomNavigationBar是Scaffold组件的参数

使用有状态组件来实现tab切换

HomePage(),CategoryPage(),SettingPage() 是tab选项卡对应的三个页面

//Tabs.dart
import 'package:flutter/material.dart';
import 'Home.dart';
import 'Category.dart';
import 'Setting.dart';
//tabs
class Tabs extends StatefulWidget {
  @override
  _TabsState createState() => _TabsState();
}

class _TabsState extends State<Tabs> {

  int _currentIndex = 0;

  List _pageList = [
    HomePage(),
    CategoryPage(),
    SettingPage()
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: this._pageList[this._currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: this._currentIndex,// 配置选中的索引值
        onTap: (int index){// tab 切换时触发的方法
          setState(() {
            this._currentIndex = index;
          });
        },
        iconSize: 36,// icon 的大小
        fixedColor: Colors.red,// 选中的颜色
        type: BottomNavigationBarType.fixed,// 配置底部tabs可以有多个按钮
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('首页'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.category),
            title: Text('分类'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            title: Text('设置'),
          ),
        ],
      ),
    );
  }
}

六、路由

1、基本路由
//路由跳转  push方式   普通路由跳转
Navigator.of(context).push(
	MaterialPageRoute(
	builder: (context)=>SearchPage(),// 跳转到SearchPage
	)
);

Navigator.of(context).pop(); // 返回上一级页面
2、命名路由
1)、配置文件
// 路由配置文件
import 'package:flutter/material.dart';
import 'package:flutter_app/pages/Detail.dart';
import 'package:flutter_app/tabs/Tabs.dart';
import 'package:flutter_app/pages/Product.dart';
import 'package:flutter_app/pages/ProductInfo.dart';
import 'package:flutter_app/pages/user/Login.dart';
import 'package:flutter_app/pages/user/Register.dart';


final routes={
  '/':(context)=>Tabs(),
  '/detail':(context,{arguments})=>DetailPage(arguments:arguments),
  '/product':(context,{arguments})=>ProductPage(arguments:arguments),
  '/product_info':(context,{arguments})=>ProductInfoPage(),
  '/login':(context)=>LoginPage(),
  '/register':(context)=>RegisterPage(),
};// 自定义命名路由



// 命名路由 固定写法
var onGenerateRoute = (RouteSettings settings){// 命名路由传值
  //统一处理
  final String name = settings.name;// 获取跳转的路由名称
  final Function pageContentBuilder = routes[name];
  if(pageContentBuilder != null){
    if(settings.arguments != null){
      final Route route = MaterialPageRoute(
          builder: (context) =>
              pageContentBuilder(context,arguments:settings.arguments));
      return route;
    }else{
    final Route route = MaterialPageRoute(
        builder: (context) =>
            pageContentBuilder(context));
    return route;
  }
  }
};
2)、传递参数
// 命名路由跳转
Navigator.pushNamed(context, '/detail',arguments: {
	"id":1
});


//Detail.dart
import 'package:flutter/material.dart';

class DetailPage extends StatelessWidget {

  final arguments;

  DetailPage({this.arguments});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("详情页面"),
      ),
      body: Column(
        children: <Widget>[
          Text("这是详情页面"),
          Text("命名路由传递的参数: ${this.arguments != null ? arguments['id'] : '0'}"),
        ],
      ),

    );
  }
}
3、替换路由

比如我们从用户中心页面跳转到了registerFirst页面,然后从registerFirst页面通过pushReplaceNamed跳转到了registerSecond页面。这个时候我们点击registerSecond的返回按钮的时候它会直接返回到用户中心

Navigator.of(context).pushReplacementNamed('/registerSecond');
4、返回根路由
1)、替换路由实现

每次进行路由跳转时都使用替换路由,则点击返回时直接返回到根路由

2)、pushAndRemoveUntil实现
Navigator.of(context).pushAndRemoveUntil(
	new MaterialPageRoute(
		builder:(context) => new Tabs(),
		(route) => route == null
	)
)

七、实现顶部TabBar

1、DefaultTabController

步骤:

1)、在Scaffold组件外嵌套一个DefaultTabController组件

2)、Scaffold组件内的appBar组件添加bottom属性

bottom: TabBar(
            tabs: <Widget>[
              Tab(text: "热门",),
              Tab(text: "推荐",)
            ],
          ),

3)、Scaffold组件内添加body属性

body: TabBarView(
          children: [
            ListView(
              children: [
                ListTile(title: Text('我是第一个tab'),),
                ListTile(title: Text('我是第一个tab'),),
                ListTile(title: Text('我是第一个tab'),)
              ],
            ),
            ListView(
              children: [
                ListTile(title: Text('我是第二个tab'),),
                ListTile(title: Text('我是第二个tab'),),
                ListTile(title: Text('我是第二个tab'),)
              ],
            ),
          ],
        ),
2、TabBarController

步骤:

1)、继承SingleTickerProviderStateMixin

2)、定义TabController

3)、初始化tabController

4)、组件中添加controller

5)、为TabController添加监听事件

import 'package:flutter/material.dart';

class TabBarControllerPage extends StatefulWidget {
  @override
  _TabBarControllerPageState createState() => _TabBarControllerPageState();
}

class _TabBarControllerPageState extends State<TabBarControllerPage>
        with SingleTickerProviderStateMixin{// 第一步  继承SingleTickerProviderStateMixin
  
  TabController _tabController;// 第二步  定义TabController

  // 生命周期函数
  @override
  void initState() {// 第三步 初始化tabController
    super.initState();
    _tabController = new TabController(length: 2, vsync: this);

    _tabController.addListener(() {
      print(_tabController.index);
      setState(() {// 添加自定义功能

      });
    });// 第五步  为TabController添加监听事件
  }

  @override
  void dispose() { // 组件销毁
    super.dispose();
    _tabController.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TabBarControllerPage'),
        bottom: TabBar(
          controller: this._tabController,// 第四步  添加controller
          tabs: [
            Tab(text: "热销",),
            Tab(text: "推荐",)
          ],
        ),
      ),
      body: TabBarView(
        controller: this._tabController,
        children: [
          Text('热销'),
          Text('推荐'),
        ],
      ),
    );
  }
}

八、侧边栏

侧边栏的主要通过Scaffold组件中的drawer(左侧边栏)和endDrawer(右侧边栏)实现

DrawerHeader(自定义侧边栏头部)

UserAccountsDrawerHeader(侧边栏头部用户模板)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值