Skip to content

Instantly share code, notes, and snippets.

@MarcinusX
Last active September 4, 2018 07:06
Show Gist options
  • Save MarcinusX/d6b8869652243d0a53ea7f169a8c69b3 to your computer and use it in GitHub Desktop.
Save MarcinusX/d6b8869652243d0a53ea7f169a8c69b3 to your computer and use it in GitHub Desktop.
BMI Calculator - Weight
import 'package:flutter/material.dart';
const TextStyle _titleStyle = TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
color: Color.fromRGBO(14, 24, 35, 1.0),
);
const TextStyle _subtitleStyle = TextStyle(
fontSize: 8.0,
fontWeight: FontWeight.w500,
color: Color.fromRGBO(78, 102, 114, 1.0),
);
class CardTitle extends StatelessWidget {
final String title;
final String subtitle;
const CardTitle(this.title, {Key key, this.subtitle}) : super(key: key);
@override
Widget build(BuildContext context) {
return RichText(
text: TextSpan(
text: title,
style: _titleStyle,
children: <TextSpan>[
TextSpan(
text: subtitle ?? "",
style: _subtitleStyle,
),
],
),
);
}
}
import 'package:bmi_calculator/card_title.dart';
import 'package:bmi_calculator/weight_slider.dart';
import 'package:bmi_calculator/widget_utils.dart' show screenAwareSize;
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class WeightCard extends StatefulWidget {
final int initialWeight;
const WeightCard({Key key, this.initialWeight}) : super(key: key);
@override
_WeightCardState createState() => _WeightCardState();
}
class _WeightCardState extends State<WeightCard> {
int weight;
@override
void initState() {
super.initState();
weight = widget.initialWeight ?? 70;
}
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.only(top: screenAwareSize(32.0, context)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
CardTitle("WEIGHT", subtitle: "(KG)"),
Expanded(
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: screenAwareSize(16.0, context)),
child: _drawSlider(),
),
),
),
],
),
),
);
}
Widget _drawSlider() {
return WeightBackground(
child: LayoutBuilder(
builder: (context, constraints) {
return constraints.isTight
? Container()
: WeightSlider(
minValue: 30,
maxValue: 110,
value: weight,
onChanged: (val) => setState(() => weight = val),
width: constraints.maxWidth,
);
},
),
);
}
}
class WeightBackground extends StatelessWidget {
final Widget child;
const WeightBackground({Key key, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
Container(
height: screenAwareSize(100.0, context),
decoration: BoxDecoration(
color: Color.fromRGBO(244, 244, 244, 1.0),
borderRadius:
new BorderRadius.circular(screenAwareSize(50.0, context)),
),
child: child,
),
SvgPicture.asset(
"images/weight_arrow.svg",
height: screenAwareSize(10.0, context),
width: screenAwareSize(18.0, context),
),
],
);
}
}
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class WeightSlider extends StatelessWidget {
WeightSlider({
Key key,
@required this.minValue,
@required this.maxValue,
@required this.value,
@required this.onChanged,
@required this.width,
}) : scrollController = new ScrollController(
initialScrollOffset: (value - minValue) * width / 3,
),
super(key: key);
final int minValue;
final int maxValue;
final int value;
final ValueChanged<int> onChanged;
final double width;
final ScrollController scrollController;
double get itemExtent => width / 3;
int _indexToValue(int index) => minValue + (index - 1);
@override
build(BuildContext context) {
int itemCount = (maxValue - minValue) + 3;
return NotificationListener(
onNotification: _onNotification,
child: new ListView.builder(
controller: scrollController,
scrollDirection: Axis.horizontal,
itemExtent: itemExtent,
itemCount: itemCount,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
int itemValue = _indexToValue(index);
bool isExtra = index == 0 || index == itemCount - 1;
return isExtra
? new Container() //empty first and last element
: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => _animateTo(itemValue, durationMillis: 50),
child: FittedBox(
child: Text(
itemValue.toString(),
style: _getTextStyle(itemValue),
),
fit: BoxFit.scaleDown,
),
);
},
),
);
}
TextStyle _getDefaultTextStyle() {
return new TextStyle(
color: Color.fromRGBO(196, 197, 203, 1.0),
fontSize: 14.0,
);
}
TextStyle _getHighlightTextStyle() {
return new TextStyle(
color: Color.fromRGBO(77, 123, 243, 1.0),
fontSize: 28.0,
);
}
TextStyle _getTextStyle(int itemValue) {
return itemValue == value
? _getHighlightTextStyle()
: _getDefaultTextStyle();
}
bool _userStoppedScrolling(Notification notification) {
return notification is UserScrollNotification &&
notification.direction == ScrollDirection.idle &&
scrollController.position.activity is! HoldScrollActivity;
}
_animateTo(int valueToSelect, {int durationMillis = 200}) {
double targetExtent = (valueToSelect - minValue) * itemExtent;
scrollController.animateTo(
targetExtent,
duration: new Duration(milliseconds: durationMillis),
curve: Curves.decelerate,
);
}
int _offsetToMiddleIndex(double offset) => (offset + width / 2) ~/ itemExtent;
int _offsetToMiddleValue(double offset) {
int indexOfMiddleElement = _offsetToMiddleIndex(offset);
int middleValue = _indexToValue(indexOfMiddleElement);
middleValue = math.max(minValue, math.min(maxValue, middleValue));
return middleValue;
}
bool _onNotification(Notification notification) {
if (notification is ScrollNotification) {
int middleValue = _offsetToMiddleValue(notification.metrics.pixels);
if (_userStoppedScrolling(notification)) {
_animateTo(middleValue);
}
if (middleValue != value) {
onChanged(middleValue); //update selection
}
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment