Skip to content

https://cloud.tencent.com/developer/article/2019869https://www.wanandroid.com/blog/show/2268https://www.wanandroid.com/blog/show/2

Scaffold

https://juejin.cn/post/6986548296357675016 Scaffold是Flutter自带的用于快速开发的框架或者称之为“脚手架”。它实现了基本的Material Design布局结构。 Scaffold常用的几个重要属性有:

  • appBar:显示在界面顶部的一个标题栏
  • body:页面的主题内容
  • bottomNavigationBar :页面的底部导航栏
  • drawer:抽屉菜单控件
  • floatingActionButton :浮动按钮

image.png

MaterialApp

可以使用ThemeData改变项目整体的样式,暗黑和白天模式

dart
class _AppRootPageState extends State<AppRootPage> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter 脚手架',
      //应用的主题
      theme: ThemeData(
        //主背景色
        primaryColor: Colors.blue,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.lightBlueAccent),
        useMaterial3: true,
      ),
      //应用程序默认显示的页面
      home: const IndexPage(),
      //debug模式下不显示debug标签
      debugShowCheckedModeBanner: false,
      //国际化语言环境
      localizationsDelegates: [],
      //配置程序语言环境
      locale: const Locale('zh', 'CN'),
    );
  }
}

image.png

AppBar

显示在APP的顶部的控件,对应Android早期的ActionBar和火来的AppBar AppBar由leading、bottom、title、actions、flexibleSpace组成 简单使用:

dart
appBar: AppBar(
  leading: IconButton(
    icon: const Icon(Icons.close),
    onPressed: (){},
  ),
  title: Text(widget.title),
  centerTitle: true,
  actions: [
    IconButton(onPressed: (){}, icon:const Icon(Icons.message)),
    IconButton(onPressed: (){}, icon:const Icon(Icons.more_horiz_sharp)),
  ],
),

实现滑动的切换标签栏

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

class Slip extends StatefulWidget {
  const Slip(String s, {Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _SlipState();

}

class _SlipState extends State<Slip> with SingleTickerProviderStateMixin {
  //控制器
  late TabController _tabController;

  //tab集合
  List<Tab> tabs = <Tab>[];

  //主题页面集合
  List<Widget>bodyList = [];

  @override
  void initState() {
    super.initState();
    //初始化tab
    tabs = <Tab>[
      const Tab(text: "新闻",),
      const Tab(text: "娱乐",),
      const Tab(text: "文化",),
      const Tab(text: "科技",),
    ];

    //创建主题页面
    for (int i = 0; i < tabs.length; ++i) {
      bodyList.add(Center(child: tabs[i],));
    }

    _tabController = TabController(length: tabs.length, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TabBar(
          tabs: tabs,
          controller: _tabController,
          isScrollable: true,
        ),
        centerTitle: true,
      ),
      body: TabBarView(
        controller: _tabController,
        children: bodyList,
      ),
    );
  }

}

image.png

文本及其样式

Text

**Text **用于显示简单样式文本,类似原生控件中的**TextView**Text的常见属性有:

  • textAlign:对齐方式,可以指定是左对齐,右对齐,还是居中
  • maxLines:指定文本的最大行数,可以搭配overflow指定超出部分的截断方式,默认是直接截断不显示
  • style:指定文本的样式,可以指定大小,风格,颜色等

TextStyle

对应Text中的style属性,用于指定文本显示的样式如颜色、字体、粗细、背景等

TextSpan

可以指定一个文本内容的不同部分按照不同的样式显示

java
const TextSpan({
  TextStyle style, 
  String text,
  List<TextSpan> children,
  GestureRecognizer recognizer,
});

其中style 和 text属性代表该文本片段的样式和内容。 children是一个TextSpan的数组,也就是说TextSpan可以包括其他TextSpan。而recognizer用于对该文本片段上用于手势进行识别处理。下面我们看一个效果(图3-4),然后用TextSpan实现它。 image.png

java
Text.rich(TextSpan(
    children: [
     TextSpan(
       text: "Home: "
     ),
     TextSpan(
       text: "https://flutterchina.club",
       style: TextStyle(
         color: Colors.blue
       ),  
       recognizer: _tapRecognizer
     ),
    ]
))
  • 上面代码中,我们通过 TextSpan 实现了一个基础文本片段和一个链接片段,然后通过Text.rich 方法将TextSpan 添加到 Text 中,之所以可以这样做,是因为 Text 其实就是 RichText 的一个包装,而RichText 是可以显示多种样式(富文本)的 widget。
  • _tapRecognizer,它是点击链接后的一个处理器(代码已省略),关于手势识别的更多内容我们将在后面单独介绍。

Image

本地图片

本地图片的存放位置,并且需要在pub文件下配置,注意缩进。添加后重新运行项目才会加载静态资源,热更新检测不到新添加的静态资源 image.png 加载本地图片,并且正方形显示会拉伸

java
Image.asset()

image.png

网络图片

java
Image.network()

圆形图片

image.png 可以使用CircleAvater()来包裹我们的图片,为图片设置圆角

dart
const CircleAvatar(backgroundImage: AssetImage("images/cat.jpg"),radius: 50,)

占位图

对于网络请求设置图片可以使用FadeInImage来设置请求失败时的占位图

java
FadeInImage(placeholder:"" image:"")

image.png

Button

ElevatedButton

漂浮按钮,它默认带有阴影。使用很简单

java
ElevatedButton(
  child: Text("normal"),
  onPressed: () {},
);

image.png

TextButton

文本按钮,默认背景透明不带阴影的按钮

OutlinedButton

OutlinedButton默认有一个边框,不带阴影且背景透明。

IconButton

IconButton是一个可点击的Icon,不包括文字,默认没有背景,点击后会出现背景,

FloatingActionButton

圆形浮动按钮 image.png

带图标的按钮

所有的按钮都有一个Icon构造函数,可以指定按钮的图标

java
ElevatedButton.icon(
  icon: Icon(Icons.send),
  label: Text("发送"),
  onPressed: _onPressed,
),
OutlinedButton.icon(
  icon: Icon(Icons.add),
  label: Text("添加"),
  onPressed: _onPressed,
),
TextButton.icon(
  icon: Icon(Icons.info),
  label: Text("详情"),
  onPressed: _onPressed,
),

StatefulWidget和setState

有状态控件和设置状态 首先Home是一个有状态的控件,它的状态的监控通过_HomeState去监听 为什么要设计无状态和有状态的控件呢? image.png

Color参数

Color.ragb0等 https://www.bilibili.com/video/BV138411B7Zz?p=12&spm_id_from=pageDriver&vd_source=2c2d0ce64b817501491ef975f77fea05

输入框

TextField控件

TextFiled类似Android原生中的EditView,用于输入文本

需要定义一个Controller接收输入框中的值 点击一下打印输入框上的内容 image.png 实现一个登录注册页面的输入框样式: image.png

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

class MyTextField extends StatefulWidget {
  const MyTextField({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _TextFiledState();
}

class _TextFiledState extends State<MyTextField> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("输入框"),
      ),
      body: Column(
        children: [
          TextField(
            obscureText: true, //隐藏文本
            maxLines: 1,
            maxLength: 12,
            decoration: InputDecoration(
              // icon: Icon(Icons.people),
              prefixIcon: Icon(Icons.people),
              hintText: '请输入账号',
              labelText: '请输入账号',
              suffixIcon:
                  IconButton(onPressed: () {}, icon: const Icon(Icons.cancel)),
              border: const OutlineInputBorder(
                borderRadius: BorderRadius.all(Radius.circular(30)),
              ),
            ),
          )
        ],
      ),
    );
  }
}

image.png

TextEditingController

文本控制器

创建控制器

dart
//文本控制器
  TextEditingController _controller = TextEditingController();

通过控制器设置TextField初始值

dart
_controller =
        TextEditingController.fromValue(const TextEditingValue(text: '初始化内容'));

绑定控制器

dart
TextField(
  controller: _controller,
  )

设置监听

dart
void setTextController() {
    //TextEditingValue发生变化时和获取失去焦点时都会调用
    _controller.addListener(() {
      text=_controller.text;
      print(text);
    });
  }
dart
class _TextFiledState extends State<MyTextField> {
  //文本控制器
  TextEditingController _controller = TextEditingController();

  //输入框上的内容
  String text = "";

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _controller =
        TextEditingController.fromValue(const TextEditingValue(text: '初始化内容'));
    setTextController();
  }

  void setTextController() {
    //TextEditingValue发生变化时和获取失去焦点时都会调用
    _controller.addListener(() {
      text=_controller.text;
      print(text);
    });
  }

  ···
  }

点击收起键盘

在外层包裹一个手势即可

dart
return GestureDetector(
    onTap: (){
      SystemChannels.textInput.invokeMethod('TextInput.hide');
    },
    child: Scaffold(
      appBar: AppBar(
        title: const Text("输入框"),
      ),
      body: Column(
        children: [
          TextField(
            controller: _controller,
            // obscureText: true,
            //隐藏文本
            maxLines: 1,
            maxLength: 12,
            decoration: InputDecoration(
              // icon: Icon(Icons.people),
              prefixIcon: const Icon(Icons.people),
              hintText: '请输入账号',
              labelText: '请输入账号',
              suffixIcon: IconButton(
                  onPressed: () {}, icon: const Icon(Icons.cancel)),
              border: const OutlineInputBorder(
                borderRadius: BorderRadius.all(Radius.circular(30)),
              ),
            ),
          )
        ],
      ),
    ),
  );
}

SafeArea

SafeArea 是 Flutter 中的一个基础小部件,用于确保其子组件不会被系统视图(如状态栏、导航栏、刘海屏等)遮挡。它通过内部的 MediaQuery 和 ViewPadding 来获取屏幕的布局边界,并相应地调整其子组件的位置。

ListView

直接使用

image.png

Builder构建

itemCount:指定数据的数量 itemBuilder:构建每一个子项 image.png

separated带分割线的构造

image.png

带ListTitle的item

image.png

弹窗

对话弹窗

通过AlertDialog构造一个对话弹窗,返回值只能是AlertDialog 对话弹窗中的按钮通过actions属性构造 按钮中的点击取消通过Navigator.pop();实现事件 image.png

底部弹窗

showModelBottomSheet()构造底部弹窗 弹窗的返回值可以自定义 image.png

更复杂的实现 image.pngimage.png