Package setup
Create a Flutter package for your measures, add the CAMS dependencies, lay out the library and generate its JSON code.
A sampling package is a Flutter package that depends on CARP Mobile Sensing (CAMS) and generates the JSON code of its classes with build_runner. Setting up the example package, barometer_sampling_package, takes six steps:
Create the package
The example reads the barometer through an existing plugin,
sensors_plus, so it has no platform code of its own and thepackagetemplate fits. For platform code of your own, use theplugintemplate instead.Delete
test/barometer_sampling_package_test.dart, which tests the template'sCalculatorclass, and replace the placeholder inLICENSEwith your license before you publish.Add the dependencies
Then set the SDK constraints to the minimums of CAMS 2.5.1 and list the platforms, as in the example package's
pubspec.yaml:pubspec.yaml flutter pub addwrites the newest versions as minimums, so your dev dependency constraints may be higher than the example's. Each entry has a reason:Entry Why sdk,flutterThe minimums of CAMS 2.5.1. The template sets your own SDK version as the minimum, which shuts out apps on older versions that CAMS supports. The generated JSON code needs Dart 3.8 or later. platformsDeclares Android and iOS, the platforms that CAMS runs on. carp_core,carp_serializableThe library imports both directly, and CAMS does not export them. json_annotationCurrent versions of json_serializablewarn when the minimum is below 4.12.0.sensors_plusThe constraint that CAMS 2.5.1 uses, so the package never asks an app for another major version than CAMS does. build_runner,json_serializableGenerate the JSON code. Apps that use the package get the generated file and never run them. publish_to: noneOnly for the example. Remove it to publish your package. Set the analysis options
Replace
analysis_options.yamlwith the example's, which adds the checks that CAMS uses:analysis_options.yaml Each option has a reason:
Option Why strict-casts,strict-inference,strict-raw-typesReport implicit casts from dynamicand missing type arguments, which compile but can fail at runtime. CAMS uses the same modes.constant_identifier_names: falseAllows measure type constants in capitals, such as AIR_PRESSURE, as in CAMS.cancel_subscriptionsReports a stream subscription that is never canceled, which keeps a sensor running after its probe stops. depend_on_referenced_packagesReports an import of a package that is not a direct dependency. use_string_in_part_of_directivesRequires a part file to name its library file, as in part of 'barometer_sampling_package.dart';.Lay out the library
Give the package one library file with a part file per concern, as the CARP sampling packages do:
barometer_sampling_package pubspec.yaml analysis_options.yaml README.md CHANGELOG.md lib barometer_sampling_package.dart barometer_package.dart barometer_data.dart barometer_configuration.dart barometer_probes.dart barometer_privacy.dart barometer_sampling_package.g.dart test json_test.dart package_test.dart probes_test.dart example pubspec.yaml lib main.dart Each page of the guide fills one part of the package:
File Holds Page lib/barometer_sampling_package.dartThe library: imports and partdirectivesPackage setup lib/barometer_data.dartAirPressureandAirPressureSummaryData classes lib/barometer_probes.dartAirPressureProbeandAirPressureSummaryProbeProbes lib/barometer_configuration.dartAirPressureSummarySamplingConfigurationSampling configurations lib/barometer_package.dartBarometerSamplingPackageSampling package class lib/barometer_privacy.dartroundAirPressureandroundAirPressureSummaryPrivacy and data formats lib/barometer_sampling_package.g.dartThe JSON functions that build_runnergeneratesPackage setup test/The unit tests Testing and publishing example/An app that runs the package Sampling package class Each part file starts with
part of 'barometer_sampling_package.dart';. Create the five part files now with only that line; the following pages fill them in. Then replace the contents oflib/barometer_sampling_package.dartwith:lib/barometer_sampling_package.dart With one library,
json_serializablewrites one generated file for all part files. Every part file can use the library's imports and the generated functions, which are private to the library. Apps import onlypackage:barometer_sampling_package/barometer_sampling_package.dart.Name the measure types
Declare each measure type as a string constant on the package class, in
lib/barometer_package.dart:Until you add its other members, the analyzer reports them as missing. For the complete class, see Sampling package class.
A measure type is a namespace and a name, joined by a period. Three rules keep it working:
- Use a namespace that you control: a reverse domain name, such as
org.mylab.sensing, not CARP'sdk.cachet.carp. A measure type is a global key, and when two packages declare it, CAMS logs only a warning. It then takes the probes from the first registered package and everything else from the last. - Use the data class name in lowercase as the name:
airpressureforAirPressure, the pattern of CARP's own types. The name must not contain periods. - Do not repeat the name earlier in the string: CAMS takes the text after the last period as the name. It then cuts the namespace off where that name first occurs.
com.example.exampletherefore turns intocom.example, andcom.example.ampleintocom.e.ample. Lookups by the original string then fail, and data is stored under the wrong type.
The same string must appear in four places, or data goes missing:
- the type of the measures in a protocol;
- a key in the package's sampling schemes;
- a case in the
switchof itscreate()method; - the
jsonTypeof the data class.
One constant keeps all four equal.
- Use a namespace that you control: a reverse domain name, such as
Generate the JSON code
After you write the data classes on the next page, run the generator in the package folder:
Run it again after each change to a class annotated with
@JsonSerializable. The generator writeslib/barometer_sampling_package.g.dartwith the JSON functions of every class in the library. To regenerate on every save while you work, rundart run build_runner watchinstead.Older guides add
--delete-conflicting-outputs.build_runner2.7.0 and later always delete conflicting outputs. Current versions ignore the flag and warn: "These options have been removed and were ignored: --delete-conflicting-outputs".Commit the generated file and publish it with the package: apps that depend on the package do not run the generator.
Troubleshooting
Run dart run build_runner build. Until lib/barometer_sampling_package.g.dart exists, the analyzer reports uri_has_not_been_generated for its part directive and undefined names such as _$AirPressureFromJson in the part files.
Raise the minimum version of json_annotation:
With a lower minimum, json_serializable prints this warning, with your constraint in it, and still generates the code:
Import carp_core with hide Smartphone:
carp_core and CAMS both define a Smartphone class. A library that imports both and uses Smartphone fails with ambiguous_import, so CAMS and its sampling packages hide the carp_core class.
Next step
Data classes
Define the classes that hold the values of each measurement.