├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── cupertino_segment_control.iml ├── lib └── cupertino_segment_control.dart ├── pubspec.yaml ├── screenshot.png └── test └── cupertino_segment_control_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea 4 | .packages 5 | .pub/ 6 | packages 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.0.3] - Readme instructions changed 2 | 3 | * Readme changes 4 | 5 | ## [0.0.2] - Refactor 6 | 7 | * Refactored class naming 8 | 9 | ## [0.0.1] - Initial release 10 | 11 | * Initial release 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Development Stopped 2 | 3 | Flutter has a Cupertino Segmentcontrol now: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/cupertino/segmented_control.dart 4 | 5 | # cupertino_segment_control 6 | 7 | A flutter package which adds the [Apple's iOS Segment Control UI element](https://developer.apple.com/ios/human-interface-guidelines/controls/segmented-controls/). 8 | 9 | ## Getting Started 10 | 11 | - Add the package to your pubspec 12 | - Add following code: 13 | ```dart 14 | new SegmentControl([ 15 | new SegmentControlItem("test1", new Text("test1")), 16 | new SegmentControlItem("test2", new Text("test2")), 17 | new SegmentControlItem("test3", new Text("test3")), 18 | ]), 19 | ``` 20 | 21 | PS: minimum of 1 `SegmentControlItem` and maximum of 3 `SegmentControlItem` due to this bug: https://github.com/flutter/flutter/issues/12583 22 | 23 | ## Showcase 24 | 25 | ![](https://github.com/theobouwman/flutter_cupertino_segment_control/blob/master/screenshot.png) 26 | -------------------------------------------------------------------------------- /cupertino_segment_control.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lib/cupertino_segment_control.dart: -------------------------------------------------------------------------------- 1 | library cupertino_segement_control; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/painting.dart'; 6 | 7 | class SegmentControlItem { 8 | SegmentControlItem(this.title, this.content); 9 | 10 | final String title; 11 | final Widget content; 12 | } 13 | 14 | abstract class SegmentControlCallbacks { 15 | void _changeTab(String title); 16 | } 17 | 18 | class SegmentControl extends StatefulWidget { 19 | SegmentControl(this.tabs, {this.activeTabIndex = 0}) 20 | : assert(tabs.length > 1 && tabs.length <= 3), 21 | assert(activeTabIndex <= tabs.length - 1); 22 | 23 | final List tabs; 24 | final int activeTabIndex; 25 | 26 | @override 27 | _SegmentControlState createState() => new _SegmentControlState(); 28 | } 29 | 30 | class _SegmentControlState extends State 31 | with SegmentControlCallbacks { 32 | int _activeTabIndex; 33 | 34 | @override 35 | void initState() { 36 | super.initState(); 37 | 38 | setState(() { 39 | _activeTabIndex = widget.activeTabIndex; 40 | }); 41 | } 42 | 43 | void _changeTab(String title) { 44 | setState(() { 45 | for (int i = 0; i < widget.tabs.length; i++) { 46 | SegmentControlItem t = widget.tabs[i]; 47 | if (t.title == title) { 48 | _activeTabIndex = i; 49 | } 50 | } 51 | }); 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | Widget activeTab = widget.tabs[_activeTabIndex].content; 57 | 58 | List<_SegmentControlItem> list = <_SegmentControlItem>[]; 59 | 60 | for (int i = 0; i < widget.tabs.length; i++) { 61 | SegmentControlItem tap = widget.tabs[i]; 62 | bool isActive = tap == widget.tabs[_activeTabIndex]; 63 | _ButtonPlace place = _ButtonPlace.start; 64 | 65 | if (i > 0 && (widget.tabs.length - 1 == i)) { 66 | place = _ButtonPlace.end; 67 | } else if (i > 0 && (widget.tabs.length - 1 > i)) { 68 | place = _ButtonPlace.middle; 69 | } 70 | 71 | list.add(new _SegmentControlItem(this, tap, place, isActive)); 72 | } 73 | 74 | return new Column( 75 | mainAxisAlignment: MainAxisAlignment.start, 76 | children: [ 77 | new Padding( 78 | child: new Row( 79 | mainAxisAlignment: MainAxisAlignment.center, 80 | children: list, 81 | ), 82 | padding: new EdgeInsets.all(12.0), 83 | ), 84 | activeTab, 85 | ], 86 | ); 87 | } 88 | } 89 | 90 | class _SegmentControlItem extends StatefulWidget { 91 | _SegmentControlItem(this.callbacks, this.buttonTab, this.place, this.isActive, 92 | {this.color = CupertinoColors.activeBlue, 93 | this.inverseColor = CupertinoColors.white}); 94 | 95 | final double _defaultBorderRadius = 3.0; 96 | 97 | final SegmentControlItem buttonTab; 98 | final SegmentControlCallbacks callbacks; 99 | final _ButtonPlace place; 100 | final bool isActive; 101 | final Color color; 102 | final Color inverseColor; 103 | 104 | @override 105 | State createState() { 106 | return new _SegmentControlItemState(color, inverseColor); 107 | } 108 | } 109 | 110 | class _SegmentControlItemState extends State<_SegmentControlItem> { 111 | _SegmentControlItemState(this.color, this.inverseColor); 112 | 113 | Color color; 114 | Color inverseColor; 115 | bool tapDown = false; 116 | 117 | BoxDecoration _boxDecoration(_ButtonPlace place) { 118 | BorderRadius radius; 119 | 120 | switch (place) { 121 | case _ButtonPlace.start: 122 | radius = new BorderRadius.only( 123 | topLeft: new Radius.circular(widget._defaultBorderRadius), 124 | bottomLeft: new Radius.circular(widget._defaultBorderRadius), 125 | ); 126 | break; 127 | case _ButtonPlace.end: 128 | radius = new BorderRadius.only( 129 | topRight: new Radius.circular(widget._defaultBorderRadius), 130 | bottomRight: new Radius.circular(widget._defaultBorderRadius), 131 | ); 132 | break; 133 | default: 134 | break; 135 | } 136 | 137 | BoxDecoration dec = new BoxDecoration( 138 | color: widget.isActive ? color : inverseColor, 139 | border: place == _ButtonPlace.middle 140 | ? new Border( 141 | top: new BorderSide(color: tapDown ? inverseColor : color), 142 | bottom: new BorderSide(color: tapDown ? inverseColor : color), 143 | ) 144 | : new Border.all(color: tapDown ? inverseColor : color), 145 | borderRadius: radius, 146 | ); 147 | 148 | return dec; 149 | } 150 | 151 | void _tabDown() { 152 | if (!widget.isActive) { 153 | setState(() { 154 | tapDown = true; 155 | final Color _backupColor = color; 156 | color = inverseColor; 157 | inverseColor = _backupColor; 158 | }); 159 | } 160 | } 161 | 162 | void _tabUp() { 163 | if (!widget.isActive) { 164 | tapDown = false; 165 | final Color _backupColor = color; 166 | color = inverseColor; 167 | inverseColor = _backupColor; 168 | } 169 | } 170 | 171 | @override 172 | Widget build(BuildContext context) { 173 | return new GestureDetector( 174 | onTapDown: (_) { 175 | _tabDown(); 176 | }, 177 | onTapUp: (_) { 178 | _tabUp(); 179 | }, 180 | onTap: () { 181 | widget.callbacks._changeTab(widget.buttonTab.title); 182 | }, 183 | child: new Container( 184 | decoration: _boxDecoration(widget.place), 185 | padding: new EdgeInsets.fromLTRB(20.0, 4.0, 20.0, 4.0), 186 | child: new Text( 187 | widget.buttonTab.title, 188 | style: new TextStyle(color: widget.isActive ? inverseColor : color), 189 | ), 190 | ), 191 | ); 192 | } 193 | } 194 | 195 | enum _ButtonPlace { start, middle, end } 196 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cupertino_segment_control 2 | description: A Cupertino-stype segment control 3 | version: 0.0.3 4 | author: Theo Bouwman 5 | homepage: https://github.com/theobouwman/flutter_cupertino_segment_control 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | 11 | dev_dependencies: 12 | test: ^0.12.0 13 | 14 | # For information on the generic Dart part of this file, see the 15 | # following page: https://www.dartlang.org/tools/pub/pubspec 16 | 17 | # The following section is specific to Flutter. 18 | flutter: 19 | 20 | # To add assets to your package, add an assets section, like this: 21 | # assets: 22 | # - images/a_dot_burr.jpeg 23 | # - images/a_dot_ham.jpeg 24 | # 25 | # For details regarding assets in packages, see 26 | # https://flutter.io/assets-and-images/#from-packages 27 | # 28 | # An image asset can refer to one or more resolution-specific "variants", see 29 | # https://flutter.io/assets-and-images/#resolution-aware. 30 | 31 | # To add custom fonts to your package, add a fonts section here, 32 | # in this "flutter" section. Each entry in this list should have a 33 | # "family" key with the font family name, and a "fonts" key with a 34 | # list giving the asset and other descriptors for the font. For 35 | # example: 36 | # fonts: 37 | # - family: Schyler 38 | # fonts: 39 | # - asset: fonts/Schyler-Regular.ttf 40 | # - asset: fonts/Schyler-Italic.ttf 41 | # style: italic 42 | # - family: Trajan Pro 43 | # fonts: 44 | # - asset: fonts/TrajanPro.ttf 45 | # - asset: fonts/TrajanPro_Bold.ttf 46 | # weight: 700 47 | # 48 | # For details regarding fonts in packages, see 49 | # https://flutter.io/custom-fonts/#from-packages -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theobouwman/flutter_cupertino_segment_control/403d358316131f1b89b6014e7de3bc30d68efc7a/screenshot.png -------------------------------------------------------------------------------- /test/cupertino_segment_control_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // TODO: add tests 3 | } 4 | --------------------------------------------------------------------------------