Flutter 仿微信支付界面

左侧是微信支付的界面,右侧是开发完成后的效果,图标是从 iconfont 上下载的。首先介绍一下本篇涉及到的组件。

带装饰效果的 Container

实际过程中我们经常会遇到一个容器需要额外的样式,例如圆角,背景色等。在 Flutter 中,对于各种容器都有一个 decoration 的属性,可以用于装饰容器。典型的用法有设置背景色、圆角、边框和阴影等,其中背景色可以使用渐变色。decoration 是一个 Decoration 对象,最常用的是 BoxDecoration,BoxDecoration 的属性如下所示:

const BoxDecoration({
    this.color,
    this.image,
    this.border,
    this.borderRadius,
    this.boxShadow,
    this.gradient,
    this.backgroundBlendMode,
    this.shape = BoxShape.rectangle,
  })

其中color为使用颜色填充容器,image 为 使用图片作为背景,border 为边框,borderRadius 为边框圆角,boxShadow 为容器阴影,gradient 使用渐变色作为背景,backgroundBlendMode 是指与容器的混合模型,默认是覆盖,shape 是背景形状,默认是矩形。其中背景部分我们一般只会选择一种。这里以上面的绿色圆弧背景为例,还加上了一点点渐变(渐变色支持多个,可以根据需要调节),示例代码如下:

return Container(
      //......
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(4.0),
        gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFF56AF6D),
              Color(0xFF56AA6D),
            ]),
      ),
  		//...
    );

这里设置了边角为圆弧,半径为4,使用渐变色填充,渐变方向为从顶部中央到底部中央,渐变色有两个。

Row 行布局和 Column列布局

这个在之前的第五篇列表篇介绍过,其中 Row 代表行布局(即子元素按一行排布),Column 代表列布局(即子元素按一列排布)。具体可以参考Flutter 入门与实战(五):来一个图文并茂的列表。

ListView列表组件

列表视图,和之前的一篇一样,只是本篇的用法不同,用于实现整个页面可以按列表的方式进行滚动,直接将各个部分组件放入到列表的 children属性中,而不是使用数组构建列表元素,有点类似滚动视图的用法。

GridView网格组件

GridView 用于将一个容器按行列划分,可以指定主轴的元素个数(根据滚动方向定),之后自动按总元素的个数分别填充到网格,例如按纵向滚动时,则可以指定行方向一行有多少个网格,每个网格一个元素。超出一行数量后会自动换另一行。最简单的用法是使用 GridView.count 方法构建 GridView,用法如下:

GridView.count(
   crossAxisSpacing: gridSpace,
   mainAxisSpacing: gridSpace,
   crossAxisCount: crossAxisCount,
   //设置以下两个参数,禁止GridView的滚动,防止与 ListView 冲突
   shrinkWrap: true,
   physics: NeverScrollableScrollPhysics(),
   children: buttons.map((item) {
      return _getMenus(item['icon'], item['name'], color: textColor);
    }).toList(),
);

这里 crossAxisSpacing 是与滚动方向垂直的元素的间距,如按纵向(默认值)滚动,则是横向行元素之间的间距。mainAxisSpacing 是与滚动方向相同的元素的间距。children 即网格中的元素。这里需要注意的是,由于 本例中GridView是嵌套在 ListView 里面的,两个组件都是纵向滚动,这样会引起冲突导致布局无法满足约束。因此,在这里设置了 shrinkWrap 为 true 和 physics 为NeverScrollableScrollPhysics,以禁止 GridView 的滚动,从而满足约束。

代码实现

首先来分析布局,所有菜单按钮其实都是一样的布局,可以使用统一的列布局完成菜单按钮,提高复用性。菜单按钮从上到下一次为图标、间距(图标与文字之间)和菜单名称。实现代码如下:

Column _getMenus(String icon, String name, {Color color = Colors.black}) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      SizedBox(
        child: Image.asset(icon),
        width: 50,
        height: 50,
      ),
      SizedBox(
        height: 5,
      ),
      Text(name, style: TextStyle(fontSize: 14.0, color: color, height: 2)),
    ],
  );

通过传输一个图标名称,菜单名称和可选的字体颜色(顶部区域和其他的文字颜色不同)来实现单个菜单。

其次来看顶部区域,顶部区域只有两个按钮,使用带装饰的容器实现背景的装饰和圆角。再采用行布局将两个菜单按钮在横向均匀排布。同时,使用 Center 布局将两个菜单保持中部居中。这里指定了容器的高度,这是因为从美观上看太矮了不太协调,实际开发要根据 UI 设计稿定。

Widget _headerGridButtons() {
    double height = 144;
    List<Map<String, String>> buttons = GridMockData.headerGrids();
    return Container(
      height: height,
      margin: EdgeInsets.fromLTRB(MARGIN, MARGIN, MARGIN, MARGIN / 2),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(4.0),
        gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFF56AF6D),
              Color(0xFF56AA6D),
            ]),
      ),
      child: Center(
        child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: buttons
                .map((item) =>
                    _getMenus(item['icon'], item['name'], color: Colors.white))
                .toList()),
      ),
    );
  }

其他菜单布局都是一样,只是区域标题,菜单数量、菜单内容不同,因此可以统一封装一个通用的方法来构建任意形式的菜单,以及设置区域标题的字体样式、圆角背景等属性。菜单均使用 GridView 实现网格式布局,同时由于菜单布局相同,可以封装一个通用的方法来指定网格一行按钮的数量,按钮字体颜色等属性,实现代码的复用。

Widget _dynamicGridButtons(List<Map<String, String>> buttons, String title,
      {int crossAxisCount = 4}) {
    return Container(
      margin: EdgeInsets.fromLTRB(MARGIN, MARGIN, MARGIN, MARGIN / 2),
      padding: EdgeInsets.all(MARGIN),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(4.0),
        color: Colors.white,
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: TextStyle(color: Colors.grey[700]),
          ),
          SizedBox(height: 20),
          _gridButtons(buttons, crossAxisCount, textColor: Colors.black),
        ],
      ),
    );
  }

GridView _gridButtons(List<Map<String, String>> buttons, int crossAxisCount,
      {Color textColor = Colors.white}) {
    double gridSpace = 5.0;
    return GridView.count(
      crossAxisSpacing: gridSpace,
      mainAxisSpacing: gridSpace,
      crossAxisCount: crossAxisCount,
      //设置以下两个参数,禁止GridView的滚动,防止与 ListView 冲突
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      children: buttons.map((item) {
        return _getMenus(item['icon'], item['name'], color: textColor);
      }).toList(),
    );
  }
}

ListView 构建完整页面:实际的整个页面很简单,只需要将各个区域放入到 ListView 的 children 属性即可,从这里也可以看出,将子组件尽可能细化,不但能够提高代码复用性,还可以降低嵌套层级,提高代码的可读性和可维护性。

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          _headerGridButtons(),
          _dynamicGridButtons(GridMockData.financeGrids(), '金融理财'),
          _dynamicGridButtons(GridMockData.serviceGrids(), '生活服务'),
          _dynamicGridButtons(GridMockData.thirdpartyGrids(), '购物消费'),
        ],
      ),
    );
  }

Mock 数据准备
按钮数据均使用 Mock 数据,这里只是返回一个 List<Map<String, String>>数组对象,对象里是每个菜单的图标文件名称和菜单名称,下面是金融服务区域的菜单 Mock方法。

static List<Map<String, String>> financeGrids() {
    return [
      {'name': '信用卡还款', 'icon': 'images/grid-buttons/grid-1-1.png'},
      {'name': '借钱', 'icon': 'images/grid-buttons/grid-1-2.png'},
      {'name': '理财', 'icon': 'images/grid-buttons/grid-1-3.png'},
      {'name': '保险', 'icon': 'images/grid-buttons/grid-1-4.png'},
    ];
  }

其他待改进的地方:从代码中可以看出,访问按钮的时候是使用 Map 对象的键来访问的,需要使用['name']或['icon']来访问,这种方式非常不利于编码,而且很容易拼写错误。因此,实际使用中应当将 Json 对象(即 Map)转换为实体类,这样就可以通过访问实体类的属性来设置菜单的参数,实际维护起来更为方便。

结语:

Flutter 提供的基础 UI 组件库能够满足绝大部分复杂页面布局,通过各种布局组件的组合即可完成。因此,熟悉基础的布局组件的特性十分重要。同时,需要注意组件的拆分和抽离完成子组件的封装,提高复用性的同时也避免了嵌套层级过深的问题。

以上就是Flutter 仿微信支付界面的实现的详细内容,更多关于Flutter 微信支付界面的资料请关注我们其它相关文章!

时间: 2021-05-29

flutter仿微信底部图标渐变功能的实现代码

先给大家展示下效果图,感觉不错请参考实例代码. 实现思路 在flutter中,如果想实现上面的页面切换效果,必然会想到pageView.pageView的controller可以监听到pageView的滚动事件,也可以获取pageView滚动的位置,所以我们在滚动事件中根据位置去改变对应的图标颜色就可以实现了. 改变图标颜色 图标是从微信中提取出来的,都是webp格式的图片.要改变图片颜色可以使用ImageIcon这个组件. ImageIcon会把一张图片变成单色图片,所以只要图片没有多色的要求

flutter 实现点击下拉栏微信右上角弹出窗功能

先看效果实现 需求分析 这个是使用 PopupRoute这个路由类进行实现 大概原理就是利用PopupRpute这个类进行改造,然后自定义一个页面,页面内镶嵌一个动画类,用来实现缩放动画 大概分为三部分,PopupRoute改造,弹出页面设置,动画类设置. 为什么选择PopupRoute? 可以镶嵌在flutter本身的路由管理之中 也就是逻辑操作都是正常的页面管理,可以手动管理,也可以用路由返回直接关掉,不会影响原有页面和布局 第一步,改造PopupRoute类 import 'package

Flutter实现仿微信底部菜单栏功能

import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: MyHomePage(), ), ); } } class MyHomePage extends Sta

Android仿微信底部菜单栏功能显示未读消息数量

底部菜单栏很重要,我看了一下很多应用软件都是用了底部菜单栏,这里使用了tabhost做了一种通用的(就是可以像微信那样显示未读消息数量的,虽然之前也做过但是layout下的xml写的太臃肿,这里去掉了很多不必要的层,个人看起来还是不错的,所以贴出来方便以后使用). 先看一下做出来之后的效果: 以后使用的时候就可以换成自己项目的图片和字体了,主框架不用变哈哈, 首先是要布局layout下xml文件 main.xml: <?xml version="1.0" encoding=&qu

Android仿微信底部菜单栏效果

前言 在市面上,大多数的APP都需要通过底部菜单栏来将程序的功能进行分类整理,通常都是分为3-5个大模块,从而正确有效地引导用户去使用我们的APP.实现底部菜单栏的方法也有很多种. 1.仿微信底部菜单栏(ViewPager+ImagerView+TextView) ......(其他方式后续会补充) 效果预览 首先来个开胃菜,看看实现效果: 先贴出项目所需的资源文件,这些可随个人自由更改颜色和文字 colors.xml <color name="bg_line_light_gray&quo

Android仿微信底部按钮滑动变色

Android仿微信底部按钮滑动变色,这里只针对使用Fragment为Tab页的滑动操作,进行简单的变色讲解. 首先说下OnPageChangeListener这个监听 //这个监听有三个方法 public abstract void onPageScrollStateChanged (int state) public abstract void onPageScrolled (int position, float positionOffset, int positionOffsetPixe

Android 仿微信底部渐变Tab效果

先来看一下效果图 除了第三个的发现Tab有所差别外,其他的基本还原了微信的底部Tab渐变效果 每个Tab都是一个自定义View,根据ImageView的tint属性来实现颜色渐变效果,tint属性的使用可以看我的上一篇文章 我将自定义View命名为ShadeView,包含四个自定义属性 意思分别为图标.背景色.底部文本.底部文本大小 <declare-styleable name="ShadeView"> <attr name="icon" for

iOS高仿微信表情输入功能代码分享

最近项目需求,要实现一个类似微信的的表情输入,于是把微信的表情扒拉出来,实现了一把.可以从这里下载源码.看起来表情输入没有多少东西,不外乎就是用NSTextAttachment来实现图文混排,结果在实现的过程中遇到了很多小问题,接下来会一一介绍遇到过的坑.先上一张效果图: 一.实现表情选择View(WKExpressionView) 具体的实现就不细说了,主要功能就是点击表情时,将对应表情的图片名称通知给delegate. 二.实现表情textView(WKExpressionTextView)

Android仿微信语音聊天功能

本文实例讲述了Android仿微信语音聊天功能代码.分享给大家供大家参考.具体如下: 项目效果如下: 具体代码如下: AudioManager.java package com.xuliugen.weichat; import java.io.File; import java.io.IOException; import java.util.UUID; import android.media.MediaRecorder; public class AudioManager { private

Android开发之微信底部菜单栏实现的几种方法汇总

 实现方式 实现的方式有很多种 这里总结最常见的几种方式,以后再添加其他的. viewPager + RadioGroup viewPager + FragmentTabHost viewpager +TabLayout viewPager+RadioGroup 感觉这是最简单的一个了,我也就不贴代码 说说我理解的思路吧 通过给pager 和RadioGroup 添加监听,监听两个控件的变化 实现联动 当viewPager的显示pager改变就会触发监听 ,在监听中选中对应的RadioButto

android 中viewpager+fragment仿微信底部TAG完美渐变

viewpager+fragment仿微信底部TAG完美渐变,在图片渐变的同时字的颜色也在变,注意,是渐变哦! 效果图: activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:zhy="http://schemas.android.com/apk/res/com.Sing.weixin" xmlns:tools="

利用百度地图Android sdk高仿微信发送位置功能及遇到的问题

接触了百度地图开发平台半个月了,这2天试着模仿了微信给好友发送位置功能,对百度地图的操作能力又上了一个台阶 我在实现这个功能的时候,遇到一些困难,可能也是别人将会遇到的困难,特在此列出 1.在微信发送功能中,不管用户如何拖拽地图,总有个覆盖物固定了MapView中央,怎么实现? 其实这很容易实现,只要MapView的布局文件中,将一个ImageView覆盖在MapView的中央,就能够实现不管用户如何拖拽地图,覆盖物(ImageView)总固定总MapView中央 2.如何获取MapView中央