flutter_thermometer 1.0.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 77

flutter_thermometer #

A flutter Thermometer widget.

![Screenshot1](./images/screenshot1.gif) ![Screenshot2](./images/screenshot2.gif) ![Screenshot3](./images/screenshot3.gif)

Installation #

Add a dependency to pubspec.yaml:

dependencies:
  flutter_thermometer: ^1.0.0

Getting Started #

For basic usage, three parameters are required: minValue, maxValue and value. The widget must be instantiated within a fully constrained size container. In the follwing example, a SizedBox is used:

SizedBox(
  width: thermoWidth,
  height: thermoHeight,
  child: Thermometer(
    value: value,
    minValue: minValue,
    maxValue: maxValue
  )
)

Anatomy of the thermometer widget #

<br/>

![Anatomy](./images/anatomy.gif)


The thermometer widget is made of several elements: - An outline, which is characterized by `thickness`, `radius`, `barWidth` and `outlineColor` parameters. - A Mercury bar which whose height designates the temperature. Its color is controlled bt the `mercuryColor` parameter. - An optional scale. - An optional label. - An optional setpoint.

Scale #

The thermometer scale comprises a set of ticks, usually shown on the left side of the thermometer, but can be also set to be shown on the right side. The scale is provided by a class implementing the ScaleProvider interface. Given a minimum and a maximum value, a ScaleProvider computes a list of ticks that are drawn on the widget. Each tick can have specific color, length and label. The package comes with a single implementaion - IntervalScaleProvider. This implementation places ticks at constant interval, starting from the minimum temperature. All the ticks have the same color, length and a temperature value as a text label.

Custom implementation can be used to show other kind of scales, for example one that has minor and major ticks.

Label #

The label is shown on the upper side of the thermometer widget, on the side opposite to the scale. It is usually used to denote the unit of measurement shown by the thermometer. A label is created by providing an instancel of ThermometerLabel to the widget. For convenience, two labels are provided by the package - degrees Celsius and degrees Farenheit, but any other custom label can be created and styled.

Setpoint #

A setpoint is a small triangle (or two triangles) denoting a target temperature. A setpoint is created by providing a Setpoint instance to the widget. The setpoint can be configured for value (temperature), color, size and side - either on the left side, the right side or both.

LICENSE #

The project is licensed under the MIT license. See the LICENSE file for details.

CHANGELOG #

1.0.0 #

  • Initial release.

example/lib/main.dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
import 'package:flutter_thermometer/thermometer.dart';
import 'custom_scale_provider.dart';

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

class ThermometerDemoApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ThermometerDemoWrapper(),
    );
  }
}

class ThermometerDemoWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) => ThermometerDemo();
}

class ThermometerDemo extends StatefulWidget {
  @override
  _ThermometerDemoState createState() => _ThermometerDemoState();
}

class _ThermometerDemoState extends State<ThermometerDemo>
    with TickerProviderStateMixin {
  bool fullscreen;
  double thermoWidth;
  double thermoHeight;
  double minValue;
  double maxValue;
  double value;
  double radius;
  double barWidth;
  double outlineThickness;
  Color outlineColor;
  Color mercuryColor;
  Color backgroundColor;
  bool useCustomScale;
  double scaleInterval;
  bool mirrorScale;
  int labelType; // 0 - Celsius; 1 - Farenheit; 2 - Custom
  bool setpointEnabled;
  Setpoint setpoint;

  @override
  void initState() {
    fullscreen = false;
    thermoHeight = 150;
    thermoWidth = 150;
    minValue = -10;
    maxValue = 70;
    value = 30;
    radius = 30.0;
    barWidth = 30.0;
    outlineThickness = 5.0;
    outlineColor = Colors.black;
    mercuryColor = Colors.red;
    backgroundColor = Colors.transparent;
    useCustomScale = false;
    scaleInterval = 10;
    mirrorScale = false;
    labelType = 0;
    setpointEnabled = false;
    setpoint =
        Setpoint(60, size: 9, color: Colors.blue, side: SetpointSide.both);

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final tabController =
        TabController(length: 5, initialIndex: 0, vsync: this);

    return Scaffold(
        appBar: AppBar(
          title: Text('Thermometer Demo'),
        ),
        body: fullscreen
            ? Center(child: _buildDUT())
            : Column(
                children: <Widget>[
                  Container(
                    height: 300,
                    child: Center(
                      child: _buildDUT(),
                    ),
                  ),
                  Container(
                    child: _buildSettings(tabController),
                  )
                ],
              ));
  }

  Widget _buildDUT() => GestureDetector(
        child: SizedBox(
          width: thermoWidth,
          height: thermoHeight,
          child: Thermometer(
              value: value,
              minValue: minValue,
              maxValue: maxValue,
              radius: radius,
              barWidth: barWidth,
              outlineThickness: outlineThickness,
              outlineColor: outlineColor,
              mercuryColor: mercuryColor,
              backgroundColor: backgroundColor,
              scale: useCustomScale
                  ? CustomScaleProvider()
                  : IntervalScaleProvider(scaleInterval),
              mirrorScale: mirrorScale,
              label: labelType == 0
                  ? ThermometerLabel.celsius()
                  : labelType == 1
                      ? ThermometerLabel.farenheit()
                      : ThermometerLabel('Custom',
                          textStyle: TextStyle(
                              color: Colors.blue,
                              fontSize: 16,
                              fontStyle: FontStyle.italic)),
              setpoint: setpointEnabled ? setpoint : null),
        ),
        onTap: () {
          setState(() {
            fullscreen = !fullscreen;
          });
        },
      );

  Widget _buildSettings(TabController tabController) => Column(
        children: <Widget>[
          Container(
            child: TabBar(
              labelColor: Colors.black,
              controller: tabController,
              tabs: <Widget>[
                Tab(
                  child: Text('Value'),
                ),
                Tab(text: 'Colors'),
                Tab(text: 'Geometry'),
                Tab(text: 'Scale'),
                Tab(text: 'Setpoint')
              ],
            ),
          ),
          Container(
            height: 250,
            child: TabBarView(
              controller: tabController,
              children: <Widget>[
                _buildValueTab(),
                _buildColorTab(),
                _buildGeometryTab(),
                _buildScaleTab(),
                _buildSetpointTab()
              ],
            ),
          )
        ],
      );

  Widget _buildValueTab() {
    return Table(
      columnWidths: {0: FixedColumnWidth(100)},
      defaultVerticalAlignment: TableCellVerticalAlignment.middle,
      children: [
        TableRow(children: [
          Text('Min Value'),
          Slider(
              min: -100,
              max: 100,
              value: minValue,
              onChanged: (sliderValue) {
                setState(() {
                  minValue = min(sliderValue, maxValue - 10);
                  value = max(value, minValue);
                  setpoint.apply(value: max(setpoint.value, minValue));
                });
              })
        ]),
        TableRow(children: [
          Text('Max Value'),
          Slider(
            min: -100,
            max: 100,
            value: maxValue,
            onChanged: (sliderValue) {
              setState(() {
                maxValue = max(sliderValue, minValue + 10);
                value = min(value, maxValue);
                setpoint.apply(value: min(setpoint.value, maxValue));
              });
            },
          )
        ]),
        TableRow(children: [
          Text('Value'),
          Slider(
            min: -100,
            max: 100,
            value: value,
            onChanged: (sliderValue) {
              setState(() {
                value = max(min(sliderValue, maxValue), minValue);
              });
            },
          )
        ])
      ],
    );
  }

  Widget _buildColorTab() {
    return Table(
      columnWidths: {0: FixedColumnWidth(100)},
      defaultVerticalAlignment: TableCellVerticalAlignment.middle,
      children: [
        TableRow(children: [
          Text('Outline'),
          CircleColor(
            color: outlineColor,
            circleSize: 35.0,
            onColorChoose: () {
              colorPickerDialog(context, outlineColor).then((newColor) {
                if (newColor != null)
                  setState(() {
                    outlineColor = newColor;
                  });
              });
            },
          )
        ]),
        TableRow(children: [
          Text('Mercury'),
          CircleColor(
              circleSize: 35.0,
              color: mercuryColor,
              onColorChoose: () {
                colorPickerDialog(context, mercuryColor).then((newColor) {
                  if (newColor != null)
                    setState(() {
                      mercuryColor = newColor;
                    });
                });
              })
        ]),
        TableRow(children: [
          Text('Backgound'),
          CircleColor(
              circleSize: 35.0,
              color: backgroundColor,
              onColorChoose: () {
                colorPickerDialog(context, backgroundColor).then((newColor) {
                  if (newColor != null)
                    setState(() {
                      backgroundColor = newColor;
                    });
                });
              })
        ])
      ],
    );
  }

  Widget _buildGeometryTab() {
    return Table(
      columnWidths: {0: FixedColumnWidth(100)},
      defaultVerticalAlignment: TableCellVerticalAlignment.middle,
      children: [
        TableRow(children: [
          Text('Height'),
          Slider(
              min: 50,
              max: 300,
              value: thermoHeight,
              onChanged: (sliderValue) {
                setState(() {
                  thermoHeight = sliderValue;
                });
              })
        ]),
        TableRow(children: [
          Text('Width'),
          Slider(
            min: 50,
            max: 300,
            value: thermoWidth,
            onChanged: (sliderValue) {
              setState(() {
                thermoWidth = sliderValue;
              });
            },
          )
        ]),
        TableRow(children: [
          Text('Thickness'),
          Slider(
            min: 1,
            max: 10,
            value: outlineThickness,
            onChanged: (sliderValue) {
              setState(() {
                outlineThickness = sliderValue;
              });
            },
          )
        ]),
        TableRow(children: [
          Text('Bar Width'),
          Slider(
            min: 10,
            max: 40,
            value: barWidth,
            onChanged: (sliderValue) {
              setState(() {
                barWidth = sliderValue;
                radius = max(radius, barWidth);
              });
            },
          )
        ]),
        TableRow(children: [
          Text('Radius'),
          Slider(
            min: 10,
            max: 40,
            value: radius,
            onChanged: (sliderValue) {
              setState(() {
                radius = sliderValue;
                barWidth = min(radius, barWidth);
              });
            },
          )
        ])
      ],
    );
  }

  Widget _buildScaleTab() {
    return Table(
        columnWidths: {0: FixedColumnWidth(70)},
        defaultVerticalAlignment: TableCellVerticalAlignment.middle,
        children: [
          TableRow(children: [
            Text('Scale Type'),
            Row(
              children: <Widget>[
                Row(children: <Widget>[
                  Radio(
                      value: true,
                      groupValue: useCustomScale,
                      onChanged: (v) {
                        setState(() {
                          useCustomScale = true;
                        });
                      }),
                  Text('Custom')
                ]),
                Row(
                  children: <Widget>[
                    Radio(
                        value: false,
                        groupValue: useCustomScale,
                        onChanged: (v) {
                          setState(() {
                            useCustomScale = false;
                          });
                        }),
                    Text('Interval')
                  ],
                )
              ],
            )
          ]),
          TableRow(children: [
            Text('Interval'),
            Slider(
              min: 5,
              max: 20,
              value: scaleInterval,
              onChanged: useCustomScale
                  ? null
                  : (sliderValue) {
                      setState(() {
                        scaleInterval = sliderValue;
                      });
                    },
            )
          ]),
          TableRow(children: [
            Text('Mirror'),
            Checkbox(
              value: mirrorScale,
              onChanged: (v) {
                setState(() {
                  mirrorScale = v;
                });
              },
            )
          ]),
          TableRow(children: [
            Text('Label'),
            Row(
              children: <Widget>[
                Row(
                  children: <Widget>[
                    Radio(
                      value: 0,
                      groupValue: labelType,
                      onChanged: (v) {
                        setState(() {
                          labelType = v;
                        });
                      },
                    ),
                    Text('Celsius')
                  ],
                ),
                Row(
                  children: <Widget>[
                    Radio(
                        value: 1,
                        groupValue: labelType,
                        onChanged: (v) {
                          setState(() {
                            labelType = v;
                          });
                        }),
                    Text('Farenheit')
                  ],
                ),
                Row(
                  children: <Widget>[
                    Radio(
                        value: 2,
                        groupValue: labelType,
                        onChanged: (v) {
                          setState(() {
                            labelType = v;
                          });
                        }),
                    Text('Custom')
                  ],
                )
              ],
            )
          ])
        ]);
  }

  Widget _buildSetpointTab() {
    return Table(
        columnWidths: {0: FixedColumnWidth(70)},
        defaultVerticalAlignment: TableCellVerticalAlignment.middle,
        children: [
          TableRow(children: [
            Text('Enabled'),
            Checkbox(
              value: setpointEnabled,
              onChanged: (v) {
                setState(() {
                  setpointEnabled = true;
                });
              },
            )
          ]),
          TableRow(children: [
            Text('Value'),
            Slider(
              min: minValue,
              max: maxValue,
              value: setpoint.value,
              onChanged: (sliderValue) {
                setState(() {
                  setpoint = setpoint.apply(value: sliderValue);
                });
              },
            )
          ]),
          TableRow(children: [
            Text('Size'),
            Slider(
              min: 5,
              max: 20,
              value: setpoint.size,
              onChanged: useCustomScale
                  ? null
                  : (sliderValue) {
                      setState(() {
                        setpoint = setpoint.apply(size: sliderValue);
                      });
                    },
            )
          ]),
          TableRow(children: [
            Text('Color'),
            CircleColor(
                circleSize: 35.0,
                color: setpoint.color,
                onColorChoose: () {
                  colorPickerDialog(context, setpoint.color).then((newColor) {
                    if (newColor != null)
                      setState(() {
                        setpoint = setpoint.apply(color: newColor);
                      });
                  });
                })
          ]),
          TableRow(children: [
            Text('Side'),
            Row(
              children: <Widget>[
                Row(
                  children: <Widget>[
                    Radio(
                      value: SetpointSide.left,
                      groupValue: setpoint.side,
                      onChanged: (v) {
                        setState(() {
                          setpoint = setpoint.apply(side: v);
                        });
                      },
                    ),
                    Text('Left')
                  ],
                ),
                Row(
                  children: <Widget>[
                    Radio(
                        value: SetpointSide.right,
                        groupValue: setpoint.side,
                        onChanged: (v) {
                          setState(() {
                            setpoint = setpoint.apply(side: v);
                          });
                        }),
                    Text('Right')
                  ],
                ),
                Row(
                  children: <Widget>[
                    Radio(
                        value: SetpointSide.both,
                        groupValue: setpoint.side,
                        onChanged: (v) {
                          setState(() {
                            setpoint = setpoint.apply(side: v);
                          });
                        }),
                    Text('Both')
                  ],
                )
              ],
            )
          ])
        ]);
  }
}

Future<Color> colorPickerDialog(BuildContext context, Color oldColor) {
  Color selectedColor = oldColor;

  return showDialog<Color>(
      context: context,
      builder: (BuildContext context) => AlertDialog(
            title: const Text('Pick a color'),
            contentPadding: EdgeInsets.all(6.0),
            content: MaterialColorPicker(
              allowShades: false,
              selectedColor: selectedColor,
              onMainColorChange: (c) {
                selectedColor = c;
              },
            ),
            actions: <Widget>[
              FlatButton(
                child: Text('CANCEL'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
              FlatButton(
                child: Text('OK'),
                onPressed: () {
                  Navigator.of(context).pop(selectedColor);
                },
              )
            ],
          ));
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_thermometer: ^1.0.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_thermometer/label.dart';
import 'package:flutter_thermometer/scale.dart';
import 'package:flutter_thermometer/setpoint.dart';
import 'package:flutter_thermometer/thermometer.dart';
import 'package:flutter_thermometer/thermometer_paint.dart';
import 'package:flutter_thermometer/thermometer_widget.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
63
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
80
Overall:
Weighted score of the above. [more]
77
Learn more about scoring.

We analyzed this package on Jul 9, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • flutter_thermometer that is a package requiring null.

Health suggestions

Format lib/label.dart.

Run flutter format to format lib/label.dart.

Format lib/scale.dart.

Run flutter format to format lib/scale.dart.

Format lib/setpoint.dart.

Run flutter format to format lib/setpoint.dart.

Format lib/thermometer_paint.dart.

Run flutter format to format lib/thermometer_paint.dart.

Format lib/thermometer_widget.dart.

Run flutter format to format lib/thermometer_widget.dart.

Maintenance suggestions

The package description is too short. (-20 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.1
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test