본문 바로가기
프로그래밍/Flutter-Dart

[Flutter] Custom Horizontal Number Picker

by 채연2 2021. 3. 23.

참고 : github.com/levent-kantaroglu/horizontal_picker

 

levent-kantaroglu/horizontal_picker

Contribute to levent-kantaroglu/horizontal_picker development by creating an account on GitHub.

github.com

위의 분 number picker를 토대로 custom 했다.

 

 

 

ListWhellScrollView 안 숫자 데이터를 세로 선으로 변경

Column(
   mainAxisSize: MainAxisSize.min,
   mainAxisAlignment: MainAxisAlignment.center,
   children: <Widget>[
      Padding(
         padding: EdgeInsets.symmetric(vertical: 10.0),
         child: Container(
            height: widget.curItem["height"],
            width: 1,
            color: Colors.black,
         ),
      ),
   ],
)

 

 

 

10마디 마다 길이 다르게 해주기

class _HorizantalPickerState extends State<HorizantalPicker> {
   ...
   List<Map> valueMap = [];

   @override
   void initState() {
      super.initState();

      for (var i = 0; i <= widget.divisions; i++) {
         int _height = 24;
         if(i % 10 == 0) {
            _height = 34;
         }
         valueMap.add({
            "value": widget.minValue +
               ((widget.maxValue - widget.minValue) / widget.divisions) * i,
            "fontSize": 14.0,
            "color": widget.passiveItemsTextColor,
            "height" : _height,
         });
      }
      ...
   }
   ...
   
   Widget build(BuildContext context) {
      return Container(
         ...
         child: Scaffold(
            ...
            body: Stack(
               children: <Widget>[
                  RotatedBox(
                     quarterTurns: 3,
                     child: ListWheelScrollView(
                        controller: _scrollController,
                        itemExtent: 10,
                           onSelectedItemChanged: (item) {
                              setState(() {
                                 int decimalCount = 1;
                                 num fac = pow(10, decimalCount);
                                 valueMap[item]["value"] =
                                    (valueMap[item]["value"] * fac).round() / fac;
                                 widget.onChanged(valueMap[item]["value"]);
                                 for (var i = 0; i < valueMap.length; i++) {
                                    int _height = 24;
                                    if(i % 10 == 0) {
                                       _height = 34;
                                    }

                                 if (i == item) {
                                    valueMap[item]["color"] = widget.activeItemTextColor;
                                    valueMap[item]["fontSize"] = 15.0;
                                    valueMap[item]["hasBorders"] = true;
                                    valueMap[item]["height"] = _height;
                                 } else {
                                    valueMap[i]["color"] = widget.passiveItemsTextColor;
                                    valueMap[i]["fontSize"] = 14.0;
                                    valueMap[i]["hasBorders"] = false;
                                    valueMap[i]["height"] = _height;
                                 }
                              }
                           });
                           ...
                        )
                        ...
                     )
                     ...
                  ]
                  ...
               )
               ...
            )
            ...
         )
         ...
      )
   }
}

 

 

 

 

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_app/utils/horizontal_picker.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Horizontal Picker',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.grey,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double _maxValue = 250;
  int _divisions = 250;
  double? newValue;


  @override
  void initState() {
    newValue = (_maxValue ~/ 2) as double;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(newValue.toString()),
          Container(
            margin: EdgeInsets.all(10),
            height: 120,
            child: HorizantalPicker(
              minValue: 0,
              maxValue: _maxValue,
              divisions: _divisions,
              onChanged: (value) {
                setState(() {
                  newValue = value;
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}

 

 

 

 

결과

 

 

 

최종코드

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

enum InitialPosition { start, center, end }

class HorizantalPicker extends StatefulWidget {
  final double minValue, maxValue;
  final int divisions;
  final Function(double) onChanged;
  final InitialPosition initialPosition;
  final Color backgroundColor;
  final bool showCursor;
  final Color cursorColor;
  final Color activeItemTextColor;
  final Color passiveItemsTextColor;
  final String? suffix;
  HorizantalPicker(
      {required this.minValue,
        required this.maxValue,
        required this.divisions,
        required this.onChanged,
        this.initialPosition = InitialPosition.center,
        this.backgroundColor = Colors.white,
        this.showCursor = true,
        this.cursorColor = Colors.red,
        this.activeItemTextColor = Colors.blue,
        this.passiveItemsTextColor = Colors.grey,
        this.suffix = ''})
      : assert(minValue < maxValue),
        assert(onChanged != null);
  @override
  _HorizantalPickerState createState() => _HorizantalPickerState();
}

class _HorizantalPickerState extends State<HorizantalPicker> {
  List<double> valueList = [];
  late FixedExtentScrollController _scrollController;

  int selectedFontSize = 14;
  List<Map> valueMap = [];

  @override
  void initState() {
    super.initState();

    for (var i = 0; i <= widget.divisions; i++) {
      int _height = 24;
      if(i % 10 == 0) {
        _height = 34;
      }
      valueMap.add({
        "value": widget.minValue +
            ((widget.maxValue - widget.minValue) / widget.divisions) * i,
        "fontSize": 14.0,
        "color": widget.passiveItemsTextColor,
        "height" : _height,
      });
    }
    setScrollController();
  }

  setScrollController() {
    int initialItem;
    switch (widget.initialPosition) {
      case InitialPosition.start:
        initialItem = 0;
        break;
      case InitialPosition.center:
        initialItem = (valueMap.length ~/ 2);
        break;
      case InitialPosition.end:
        initialItem = valueMap.length - 1;
        break;
    }

    _scrollController = FixedExtentScrollController(initialItem: initialItem);
  }

  @override
  Widget build(BuildContext context) {
    // _scrollController.jumpToItem(curItem);
    return Container(
      padding: EdgeInsets.all(3),
      margin: EdgeInsets.all(20),
      height: 150,
      alignment: Alignment.center,
      child: Scaffold(
        backgroundColor: widget.backgroundColor,
        body: Stack(
          children: <Widget>[
            RotatedBox(
              quarterTurns: 3,
              child: ListWheelScrollView(
                  controller: _scrollController,
                  itemExtent: 10,
                  onSelectedItemChanged: (item) {
                    setState(() {
                      int decimalCount = 1;
                      num fac = pow(10, decimalCount);
                      valueMap[item]["value"] =
                          (valueMap[item]["value"] * fac).round() / fac;
                      widget.onChanged(valueMap[item]["value"]);
                      for (var i = 0; i < valueMap.length; i++) {
                        int _height = 24;
                        if(i % 10 == 0) {
                          _height = 34;
                        }

                        if (i == item) {
                          valueMap[item]["color"] = widget.activeItemTextColor;
                          valueMap[item]["fontSize"] = 15.0;
                          valueMap[item]["hasBorders"] = true;
                          valueMap[item]["height"] = _height;
                        } else {
                          valueMap[i]["color"] = widget.passiveItemsTextColor;
                          valueMap[i]["fontSize"] = 14.0;
                          valueMap[i]["hasBorders"] = false;
                          valueMap[i]["height"] = _height;
                        }
                      }
                    });
                    setState(() {});
                  },
                  children: valueMap.map((Map curValue) {
                    //print("q");
                    //print(widget.backgroundColor.toString());
                    return ItemWidget(curValue,
                        backgroundColor: widget.backgroundColor,
                        suffix: widget.suffix!);
                  }).toList()),
            ),
            Visibility(
              visible: widget.showCursor,
              child: Container(
                alignment: Alignment.center,
                padding: EdgeInsets.all(5),
                child: Container(
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.all(Radius.circular(10)),
                      color: widget.cursorColor.withOpacity(0.3)),
                  width: 3,
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

class ItemWidget extends StatefulWidget {
  final Map curItem;
  final Color backgroundColor;
  final String? suffix;
  ItemWidget(this.curItem, {required this.backgroundColor, this.suffix});

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

class _ItemWidgetState extends State<ItemWidget> {
  @override
  void initState() {
    super.initState();
    int decimalCount = 1;
    num fac = pow(10, decimalCount);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
      decoration: BoxDecoration(
        color: widget.backgroundColor,
        borderRadius: BorderRadius.circular(10),
      ),
      child: RotatedBox(
        quarterTurns: 1,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.symmetric(vertical: 10.0),
              child: Container(
                height: widget.curItem["height"],
                width: 1,
                color: Colors.black,
              ),
            ),
          ],
        ),
      ),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:flutter_app/utils/horizontal_picker.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Horizontal Picker',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.grey,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double _maxValue = 250;
  int _divisions = 250;
  double? newValue;


  @override
  void initState() {
    newValue = (_maxValue ~/ 2) as double;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(newValue.toString()),
          Container(
            margin: EdgeInsets.all(10),
            height: 120,
            child: HorizantalPicker(
              minValue: 0,
              maxValue: _maxValue,
              divisions: _divisions,
              onChanged: (value) {
                setState(() {
                  newValue = value;
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}
320x100

'프로그래밍 > Flutter-Dart' 카테고리의 다른 글

[Flutter] Login App (2)  (0) 2021.03.24
[Flutter] Vlc Player & Circular Percent Indicator  (0) 2021.03.24
[Flutter] Horizontal Number Picker  (0) 2021.03.23
[Flutter] DatePicker (iOS style)  (0) 2021.03.23
[Flutter] Image Picker  (2) 2021.03.23

댓글