Flutter开发笔记

Flutter开发笔记

1. 目录结构

文件夹作用
androidAndroid平台相关代码
iosiOS平台相关代码
libFlutter相关代码,我们主要编写的代码就在这个文件夹
test用于存放测试代码
pubspec.yaml配置文件,一般存放第三方库的依赖

2. 入口文件和入口方法

每一个flutter项目的lib目录里都有一个main.dart入口文件

引入material.dart

import 'package:flutter/material.dart';

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

简写:

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

main方法是dart的入口方法,runApp方法是flutter的入口方法。

3. 自定义组件

MyApp是一个自定义组件。

// 自定义组件
class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
        child: Text(
          'Hello 112',
          textDirection: TextDirection.ltr,

          style: TextStyle(
            fontSize: 40.0,
            // color: Colors.yellow,
            color: Color.fromRGBO(244, 233, 121, 0.5),
          ),
        )
    );
  }
  
}

StatelessWidget 无状态组件,状态不可变的widget(快捷键stless)

StatefulWidget 有状态组件,持有的状态可能在widget的生命周期改变(快捷键stful)

在flutter中所有和数字相关的参数都是double类型

4. 使用MaterialApp和Scaffold两个组件装饰App

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

    常用属性:

    home(主页)title(标题)color(颜色)theme(主题)routes(路由)…

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

    Scaffold有下面几个主要属性:
    appBar -显示在界面顶部的一个 AppBar。

    body -当前界面所显示的主要内容 Widget。
    drawer -抽屉菜单控件。

使用o在Android和iOS模式之间进行切换

在Android Studio中,点击右侧Flutter Inspector -> Platform 可在Android和iOS之间切换

class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Demo'),
        ),
        body: HomeContent(),
      ),
      theme: ThemeData(
        primarySwatch: Colors.yellow
      ),
    );
  }
  
}

5. 有状态组件

构造方法:使用快捷键stful

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return ...;
  }
}

setState()改变State的方法

BottomNavigationBarItem现在使用label来替代title

BottomNavigationBarItem(
  icon: Icon(Icons.home),
  label: '首页',
),

6. 普通路由跳转

RaisedButton(
  child: Text("跳转到搜索页面"),
  onPressed: () {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => SearchPage()
      )
    );
  };
);

Search.dart

import 'package:flutter/material.dart';

class SearchPage extends StatelessWidget {
  const SearchPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.home),
        onPressed: () {
          Navigator.of(context).pop();
        },
      ),
      appBar:AppBar(
        title: Text("搜索页面"),
      ) ,
      body: Text("搜索页面内容区域"),
    );
  }
}

7. 命名路由传参

  1. 配置路由(main.dart)

    import 'package:flutter_test_app/pages/tabs/Search.dart';
    
    final routes = {
        '/search': (context, {arguments}) => SearchPage(arguments: arguments),
    };
    
  2. 通过onGenerateRoute属性统一处理(main.dart)

    // ignore: missing_return
    onGenerateRoute:(RouteSettings settings) {
      // 统一处理
      final String name = settings.name;
      final Function pageContentBuilder = this.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;
        }
      }
    }
    
  3. 发送参数(Home.dart)

    Navigator.pushNamed(context, '/search', arguments: {
      "id": 123
    });
    

    不需要传参时正常调用即可

    Navigator.pushNamed(context, '/product');
    
  4. 接收参数(Search.dart)

    final arguments;
    SearchPage({this.arguments});
    
    ...
      body: Text("搜索页面内容区域${arguments != null ? arguments['id'] : 0}"),
    ...
    

8. 封装路由

将上面的路由配置全部封装到/lib/routes/Routes.dart中

import 'package:flutter/material.dart';
import 'package:flutter_test_app/pages/Tabs.dart';
import 'package:flutter_test_app/pages/tabs/ProductInfo.dart';
import 'package:flutter_test_app/pages/tabs/Search.dart';

final routes = {
  '/': (context, {arguments}) => Tabs(),
  '/search': (context, {arguments}) => SearchPage(arguments: arguments),
  '/productInfo': (context, {arguments}) => ProductInfoPage(arguments: arguments),
};

// ignore: missing_return, top_level_function_literal_block
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;
    }
  }
};

main.dart

import 'package:flutter_test_app/routes/Routes.dart';
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      initialRoute: '/',  // 初始化时加载的路由
      onGenerateRoute: onGenerateRoute
    );
  }
}

在Search.dart中有一个按钮,点击后可跳转到/productInfo

...
    RaisedButton(
      child: Text("跳转到商品详情"),
      onPressed: () {
        Navigator.pushNamed(context, "/productInfo", arguments: {
          "pid": 456,
          "pname": "iPhone"
        });
      }
    )
...

ProductInfo.dart

import 'package:flutter/material.dart';

class ProductInfoPage extends StatefulWidget {
  Map arguments;
  ProductInfoPage({this.arguments});

  @override
  _ProductInfoPageState createState() => _ProductInfoPageState(arguments: this.arguments);
}

class _ProductInfoPageState extends State<ProductInfoPage> {
  Map arguments;
  _ProductInfoPageState({this.arguments});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品详情"),
      ),
      body: Container(
        child: Text("商品详情PID: ${arguments["pid"]}---PNAME: ${arguments["pname"]}"),
      ),
    );
  }
}
另一种方法:

我们并不需要把参数在创建state实例的时候传入,state可以直接访问widget.channelName获取到组件的属性。

传入参数:

Navigator.push(
          context,
          MaterialPageRoute(
              builder: (context) => new CallPage(
                    channelName: _channelController.text,
                  )));

接收参数:

class CallPage extends StatefulWidget {
  final String channelName;
  const CallPage({Key key, this.channelName}) : super(key: key);

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

class _CallPageState extends State<CallPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.channelName),
      ),
      backgroundColor: Colors.black,
      body: Center(
        child: Stack(
          children: <Widget>[],
        ),
      ),
    );
  }
}

9. 替换路由和返回根路由

替换路由

Navigator.of(context).pushReplacementNamed('...');

返回根路由(跳转并清空路由栈)

Navigator.of(context).pushAndRemoveUntil(new MaterialPageRoute(builder: (context) => new Tabs()), (route) => route == null);

返回到指定的Tab页面,加一个参数index传递

Tabs.dart

class Tabs extends StatefulWidget {
  final index;
  Tabs({Key key, this.index = 0}) : super(key: key);

  @override
  _TabsState createState() => _TabsState(this.index);
}

class _TabsState extends State<Tabs> {
  int _currentIndex;

  _TabsState(index) {
    this._currentIndex = index;
  }
  
  ...
      
}
Navigator.of(context).pushAndRemoveUntil(new MaterialPageRoute(builder: (context) => new Tabs(index: 1)), (route) => route == null);

10. 使用TabController实现自定义TabBar

  • 被创建的组件要继承StatefulWidget和SingleTickerProviderStateMixin
  • 创建一个TabController,在生命周期initState时初始化
  • 在调用TabBar和TabBarView时都要配置controller
import 'package:flutter/material.dart';

class CategoryPage extends StatefulWidget {
  @override
  _CategoryPageState createState() => _CategoryPageState();
}

// 继承SingleTickerProviderStateMixin
class _CategoryPageState extends State<CategoryPage> with SingleTickerProviderStateMixin {

  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = new TabController(length: 2, vsync: this);
    // 可以在这里加入一些自定义方法,如监听事件
    _tabController.addListener(() {
      print(_tabController.index);
    });
  }

    
  // 生命周期结束时摧毁,可省略
  @override
  void dispose() {
    super.dispose();
    _tabController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("TabBarControllerPage"),
        bottom: TabBar(
          controller: this._tabController,
          tabs: <Widget>[
            Tab(text: "热销",),
            Tab(text: "推荐",),
          ],
        ),
      ),
      body: TabBarView(
        controller: this._tabController,
        children: <Widget>[
          Center(child: Text("热销")),
          Center(child: Text("推荐")),
        ],
      ),
    );
  }
}

11. 侧边栏

drawer: Drawer(
  child: Column(
    children: <Widget>[
      Row(
        children: [
          Expanded(
            child: UserAccountsDrawerHeader(
              accountName: Text("admin"),
              accountEmail: Text("123@qq.com"),
              currentAccountPicture: CircleAvatar(
                backgroundImage: NetworkImage("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=4131746241,2477555401&fm=11&gp=0.jpg"),
              ),
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: NetworkImage("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1604332250193&di=a70283d0c2606afe5d7741a0762f839d&imgtype=0&src=http%3A%2F%2Fbenyouhuifile.it168.com%2Fforum%2Fday_100429%2F1004291840008539c8ac02cd5c.jpg"),
                  fit: BoxFit.cover
                )
              ),
            ),
          )
        ],
      ),
      ListTile(
        leading: CircleAvatar(
          child: Icon(Icons.home_outlined),
        ),
        title: Text("我的空间"),
      ),
      ListTile(
        leading: CircleAvatar(
          child: Icon(Icons.people),
        ),
        title: Text("用户中心"),
        onTap: () {
          Navigator.of(context).pop(); // 隐藏侧边栏
          Navigator.pushNamed(context, "/user");
        },
      ),
      ListTile(
        leading: CircleAvatar(
          child: Icon(Icons.settings),
        ),
        title: Text("设置中心"),
      ),
    ],
  ),
),

12. 两种循环建立ListView的方法

ListView(
	children: this._list.map((value) {
		return ListTile(
			title: Text(value["title"]),
		);
	}).toList(),
)

利用ListView.builder

ListView.builder(
	itemCount: this._list.length,
    itemBuilder: (context, index) {
        return ListTile(
        	title: Text("${this._list[index]["title"]}"),
        )
    }
)

13. JSON与Map类型转换

import 'dart:convert'
    
var mapData = {"name":"张三", "age":"20"};
var strData = `{"name":"张三", "age":"20"}`;

print(json.encode(mapData));  // Map转换成Json字符串
print(json.decode(strData)); 成Map类型

14. 使用http库请求接口

import 'dart:convert';
import 'package:http/http.dart' as http;

_getData() async {
    var apiUrl = "http://a.itying.com/api/productlist";
    var response = await http.get(apiUrl);
    if (response.statusCode == 200) {
      print(response.body);
      setState(() {
        this._list = json.decode(response.body)["result"];
      });
    } else {
      print("Failed${response.statusCode}");
    }
}

15. 使用dio库请求接口

import 'dart:convert';
import 'package:dio/dio.dart';

_getData() async {
    var apiUrl = 'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1';
    Response response = await Dio().get(apiUrl);
    print(json.decode(response.data)["result"]);
    setState(() {
      this._list = json.decode(response.data)["result"];
    });
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值