Flutter CupertinoPicker组件的二次封装
前言
今年大部分时间都放在了Flutter开发上,开发中自己也封装了些组件,最近有时间可以整理一下。分享给大家同时也再次整理当时的思路。
SinglePickerWidget
SinglePickerWidget是我封装的组件之一,主要是为了实现UI设计的picker效果,效果图如下,需要单位和值分开:
正常的Flutter CupertinoPicker组件是没办法实现的:
所以对CupertinoPicker组件进行了二次封装。
功能实现思路
首先说下我的实现思路,是把单位用Position组件包裹,然后进行定位到选中的一行位置。这样滑动单位的区域也会滑动SinglePickerWidget组件。
代码
由于是为了实现UI设计需求,所以组件没有做的很灵活,具体使用时还需要更改部分代码。
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
///
/// Author: chengzan
/// Date: 2020-6-8
/// Describe: 单个选择picker
///
class SinglePickerWidget extends StatefulWidget {
final List<Map> values;
final value;
final double itemHeight;
final double height;
final double width;
final String unit;
final Function onChanged;
final Color backgroundColor;
const SinglePickerWidget(
{Key key,
@required this.values,
@required this.value,
@required this.onChanged,
this.unit,
this.itemHeight = 37.5,
this.backgroundColor = const Color(0xffffffff),
this.height = 150.0,
this.width = 150.0})
: super(key: key);
@override
_SinglePickerWidgetState createState() => _SinglePickerWidgetState();
}
class _SinglePickerWidgetState extends State<SinglePickerWidget> {
int _selectedColorIndex = 0;
FixedExtentScrollController scrollController;
var values;
var value;
//设置防抖周期为300毫秒
Duration durationTime = Duration(milliseconds: 300);
Timer timer;
@override
void initState() {
super.initState();
values = widget.values;
value = widget.value;
getDefaultValue();
scrollController =
FixedExtentScrollController(initialItem: _selectedColorIndex);
}
@override
void dispose() {
super.dispose();
scrollController.dispose();
timer?.cancel();
}
// 获取默认选择值
getDefaultValue() {
// 查找要选择的默认值
for (var i = 0; i < values.length; i++) {
if (values[i]["value"] == value) {
setState(() {
_selectedColorIndex = i;
});
break;
}
}
}
// 触发值改变
void _changed(index) {
timer?.cancel();
timer = new Timer(durationTime, () {
// 触发回调函数
widget.onChanged(values[index]["value"]);
});
}
Widget _buildColorPicker(BuildContext context) {
return Container(
height: widget.height,
color: Colors.white,
child: Stack(
alignment: Alignment.center,
children: [
widget.unit != null
? Positioned(
top: widget.height / 2 - (widget.itemHeight / 2),
left: widget.width / 2 + 18.0,
child: Container(
alignment: Alignment.center,
height: widget.itemHeight,
child: Text(
widget.unit,
style: TextStyle(
color: Color(0xff333333),
fontSize: 16.0,
height: 1.5,
fontWeight: FontWeight.w500,
),
),
),
)
: Offstage(
offstage: true,
),
CupertinoPicker(
magnification: 1.0, // 整体放大率
scrollController:
scrollController, // 用于读取和控制当前项的FixedxtentScrollController
itemExtent: widget.itemHeight, // 所有子节点 统一高度
useMagnifier: true, // 是否使用放大效果
backgroundColor: Colors.transparent,
onSelectedItemChanged: (int index) {
// 当正中间选项改变时的回调
if (mounted) {
print('index--------------$index');
_changed(index);
}
},
children: List<Widget>.generate(values.length, (int index) {
return Container(
alignment: Alignment.center,
height: widget.itemHeight,
child: Text(
values[index]["label"],
style: TextStyle(
color: Color(0xff333333),
fontSize: 21.0,
height: 1.2,
fontWeight: FontWeight.w500,
),
),
);
}),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: widget.height,
width: widget.width,
child: CupertinoPageScaffold(
child: Container(
child: ListView(
padding: EdgeInsets.all(0),
children: <Widget>[
_buildColorPicker(context),
],
),
),
),
);
}
}
使用实例
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Map> values = [
{"label": "1", "value": 1},
{"label": "2", "value": 2},
{"label": "3", "value": 3},
{"label": "4", "value": 4},
{"label": "5", "value": 5}
];
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
constraints: BoxConstraints(maxWidth: 160),
alignment: Alignment.centerLeft,
child: SinglePickerWidget(
values: values,
value: 2,
width: 150,
itemHeight: 50,
height: 250,
unit: 'm',
onChanged: (val) {
print('val----$val');
},
),
)
],
);
}
}
///
/// Author: chengzan
/// Date: 2020-6-8
/// Describe: 单个选择picker
///
class SinglePickerWidget extends StatefulWidget {
final List<Map> values;
final value;
final double itemHeight;
final double height;
final double width;
final String unit;
final Function onChanged;
final Color backgroundColor;
const SinglePickerWidget(
{Key key,
@required this.values,
@required this.value,
@required this.onChanged,
this.unit,
this.itemHeight = 37.5,
this.backgroundColor = const Color(0xffffffff),
this.height = 150.0,
this.width = 150.0})
: super(key: key);
@override
_SinglePickerWidgetState createState() => _SinglePickerWidgetState();
}
class _SinglePickerWidgetState extends State<SinglePickerWidget> {
int _selectedColorIndex = 0;
FixedExtentScrollController scrollController;
var values;
var value;
//设置防抖周期为300毫秒
Duration durationTime = Duration(milliseconds: 300);
Timer timer;
@override
void initState() {
super.initState();
values = widget.values;
value = widget.value;
getDefaultValue();
scrollController =
FixedExtentScrollController(initialItem: _selectedColorIndex);
}
@override
void dispose() {
super.dispose();
scrollController.dispose();
timer?.cancel();
}
// 获取默认选择值
getDefaultValue() {
// 查找要选择的默认值
for (var i = 0; i < values.length; i++) {
if (values[i]["value"] == value) {
setState(() {
_selectedColorIndex = i;
});
break;
}
}
}
// 触发值改变
void _changed(index) {
timer?.cancel();
timer = new Timer(durationTime, () {
// 回调函数
widget.onChanged(values[index]["value"]);
});
}
Widget _buildColorPicker(BuildContext context) {
return Container(
height: widget.height,
color: Colors.white,
child: Stack(
alignment: Alignment.center,
children: [
widget.unit != null
? Positioned(
top: widget.height / 2 - (widget.itemHeight / 2),
left: widget.width / 2 + 18.0,
child: Container(
alignment: Alignment.center,
height: widget.itemHeight,
child: Text(
widget.unit,
style: TextStyle(
color: Color(0xff333333),
fontSize: 16.0,
height: 1.5,
fontWeight: FontWeight.w500,
),
),
),
)
: Offstage(
offstage: true,
),
CupertinoPicker(
magnification: 1.0, // 整体放大率
scrollController:
scrollController, // 用于读取和控制当前项的FixedxtentScrollController
itemExtent: widget.itemHeight, // 所以子节点 统一高度
useMagnifier: true, // 是否使用放大效果
backgroundColor: Colors.transparent,
onSelectedItemChanged: (int index) {
// 当正中间选项改变时的回调
if (mounted) {
print('index--------------$index');
_changed(index);
}
},
children: List<Widget>.generate(values.length, (int index) {
return Container(
alignment: Alignment.center,
height: widget.itemHeight,
child: Text(
values[index]["label"],
style: TextStyle(
color: Color(0xff333333),
fontSize: 21.0,
height: 1.2,
fontWeight: FontWeight.w500,
),
),
);
}),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: widget.height,
width: widget.width,
child: CupertinoPageScaffold(
child: Container(
child: ListView(
padding: EdgeInsets.all(0),
children: <Widget>[
_buildColorPicker(context),
],
),
),
),
);
}
}