Argument Type Mismatch -using Mapbox API - flutter

Good day,
I am currently learning flutter under the Udemy course and at the moment is implementing maps in the app. The current lecture is using Google Maps to add markers on it when user taps on the screen, however I use a different API (Mapbox) and try to implement the same output.
Currently here is the issue I have encountered when I try to compile my code:enter image description here
My code can be found here:
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:native_func_app/models/place.dart';
import '../models/place.dart';
class MapScreen extends StatefulWidget {
//const MapScreen({ Key? key }) : super(key: key);
final PlaceLocation initialLocation;
final bool isSelecting;
MapScreen({
this.initialLocation =
const PlaceLocation(latitude: 37.421, longitude: -122.084),
this.isSelecting = false,
});
#override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
LatLng _pickedLocation;
void _selectLocation(LatLng position) {
setState(() {
_pickedLocation = position;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your Map'),
),
body: FlutterMap(
options: MapOptions(
center: LatLng(widget.initialLocation.latitude,
widget.initialLocation.longitude),
zoom: 16.0,
//onTap: widget.isSelecting ? _selectLocation: null,
onTap: widget.isSelecting ? _selectLocation : () {},
),
layers: [
TileLayerOptions(
urlTemplate:
"https://api.mapbox.com/styles/v1/dhe/ckwd8qofr09gd14oq5d8djor9/tiles/256/{z}/{x}/{y}#2x?access_token=pk.eyJ1IjoiZGhlIiwiYSI6ImNrdnFybGw4ZzI1cWgycm91MXBpcW9oN3kifQ.fzK2_BFZrGU_vMpwTuU2Kg",
/*subdomains: ['a', 'b', 'c'],
attributionBuilder: (_) {
return Text("© OpenStreetMap contributors");
},*/
additionalOptions: {
'accessToken':
'pk.eyJ1IjoiZGhlIiwiYSI6ImNrdnFybGw4ZzI1cWgycm91MXBpcW9oN3kifQ.fzK2_BFZrGU_vMpwTuU2Kg',
'id': 'mapbox.mapbox-streets-v8',
}),
MarkerLayerOptions(
markers: _pickedLocation == null
? []
: [
Marker(
point: _pickedLocation,
builder: (ctx) => Container(
child: FlutterLogo(),
),
),
],
),
],
),
);
}
}
Right now, someone ask me to replace the null code with (){} to see if the error goes away and it does. However a new issue occurs when I recompile as seen here: 2nd issue crash at compile time
Can anyone help me identify the issue?
If you need to access my project ill link the Github Project I am using to view the source codes:
https://github.com/DheDeveloperUsv/native_func_app
Any help or insight to resolve this issue is very much welcome.

Add a parameter tapPosition to your _selectLocation
void _selectLocation(TapPosition tapPosition, LatLng position) {
setState(() {
_pickedLocation = position;
});
}
onTap needs a function with 2 Parameters and no return value (void).
So this won't be working:
widget.isSelecting ? _selectLocation : () {}
_selecteLocation is a function this 1 parameter
and (){} has no parameters. change (){} to (TapPosition tapPosition, LatLng position){}
complete code:
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:native_func_app/models/place.dart';
import '../models/place.dart';
class MapScreen extends StatefulWidget {
//const MapScreen({ Key? key }) : super(key: key);
final PlaceLocation initialLocation;
final bool isSelecting;
MapScreen({
this.initialLocation =
const PlaceLocation(latitude: 37.421, longitude: -122.084),
this.isSelecting = false,
});
#override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
LatLng _pickedLocation;
void _selectLocation(TapPosition tapPosition, LatLng position) {
setState(() {
_pickedLocation = position;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your Map'),
),
body: FlutterMap(
options: MapOptions(
center: LatLng(widget.initialLocation.latitude,
widget.initialLocation.longitude),
zoom: 16.0,
//onTap: widget.isSelecting ? _selectLocation: null,
onTap: widget.isSelecting ? _selectLocation : (TapPosition tapPosition, LatLng position) {},
),
layers: [
TileLayerOptions(
urlTemplate:
"https://api.mapbox.com/styles/v1/dhe/ckwd8qofr09gd14oq5d8djor9/tiles/256/{z}/{x}/{y}#2x?access_token=pk.eyJ1IjoiZGhlIiwiYSI6ImNrdnFybGw4ZzI1cWgycm91MXBpcW9oN3kifQ.fzK2_BFZrGU_vMpwTuU2Kg",
/*subdomains: ['a', 'b', 'c'],
attributionBuilder: (_) {
return Text("© OpenStreetMap contributors");
},*/
additionalOptions: {
'accessToken':
'pk.eyJ1IjoiZGhlIiwiYSI6ImNrdnFybGw4ZzI1cWgycm91MXBpcW9oN3kifQ.fzK2_BFZrGU_vMpwTuU2Kg',
'id': 'mapbox.mapbox-streets-v8',
}),
MarkerLayerOptions(
markers: _pickedLocation == null
? []
: [
Marker(
point: _pickedLocation,
builder: (ctx) => Container(
child: FlutterLogo(),
),
),
],
),
],
),
);
}
}

Related

How can I get tapped location in flutter_map?

I'm implementing a map in my code. So far this is what I have:
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tappable_polyline/flutter_map_tappable_polyline.dart';
import 'package:latlong/latlong.dart';
import 'package:geolocator/geolocator.dart';
class MapPicker extends StatefulWidget {
MapPicker({Key key}) : super(key: key);
#override
_MapPicker createState() {
return _MapPicker();
}
}
class _MapPicker extends State<MapPicker> {
Position _position;
void getLocation() async {
var position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
print(position);
setState(() {
_position = position;
});
}
#override
Widget build(BuildContext context) {
if(_position == null)
getLocation();
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text('Escoger Localización'),
),
body: _position != null ? FlutterMap(
options: MapOptions(
center: LatLng(18.0119098, -66.6159138), //Change to _position
zoom: 15.0
),
layers: [
TileLayerOptions(
urlTemplate:
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: ['a', 'b', 'c']),
MarkerLayerOptions(
markers: [],
),
TappablePolylineLayerOptions(
onTap: (TaggedPolyline polyline) => print(polyline.tag)
)
],
)
:
CircularProgressIndicator(),
);
}
}
I'm using a set LatLon for now for test purposes but the idea is using _position to use the current position when opening the map. But, my problem is the following: I want to be able to tap any place in the map and get the coordinates for the place I tapped. Is this possible?
options: new MapOptions(
onTap: (tapPosition, point) => {
print(point.toString()),
},
center: LatLng(0, 0),
zoom: 1,
maxZoom: 19,
),
This is possible through the onTap() callback I think available in MapOptions(). It returns the position tapped.

How to use rive's state machine in flutter?

I've created a .riv file with 3 state animations: start, processing, end, which are in "State machine". Rive team recently announced a new feature with dinamically changing animations, it's "State machine". Not sure, how to use it in flutter project, i.e how to dynamically change value of animation. If somebody needs some code, no problem, I could provide. Moreover, link to rive's "state machine" https://www.youtube.com/watch?v=0ihqZANziCk. I didn't find any examples related to this new feature. Please help! Thanks.
The other answer is outdated.
class SimpleStateMachine extends StatefulWidget {
const SimpleStateMachine({Key? key}) : super(key: key);
#override
_SimpleStateMachineState createState() => _SimpleStateMachineState();
}
class _SimpleStateMachineState extends State<SimpleStateMachine> {
SMITrigger? _bump;
void _onRiveInit(Artboard artboard) {
final controller = StateMachineController.fromArtboard(artboard, 'bumpy');
artboard.addController(controller!);
_bump = controller.findInput<bool>('bump') as SMITrigger;
}
void _hitBump() => _bump?.fire();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Simple Animation'),
),
body: Center(
child: GestureDetector(
child: RiveAnimation.network(
'https://cdn.rive.app/animations/vehicles.riv',
fit: BoxFit.cover,
onInit: _onRiveInit,
),
onTap: _hitBump,
),
),
);
}
}
See the RIVE guide:
https://help.rive.app/runtimes/state-machines
There are examples on rives pub package site. Here is one for state machine.
example_state_machine.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:rive/rive.dart';
/// An example showing how to drive two boolean state machine inputs.
class ExampleStateMachine extends StatefulWidget {
const ExampleStateMachine({Key? key}) : super(key: key);
#override
_ExampleStateMachineState createState() => _ExampleStateMachineState();
}
class _ExampleStateMachineState extends State<ExampleStateMachine> {
/// Tracks if the animation is playing by whether controller is running.
bool get isPlaying => _controller?.isActive ?? false;
Artboard? _riveArtboard;
StateMachineController? _controller;
SMIInput<bool>? _hoverInput;
SMIInput<bool>? _pressInput;
#override
void initState() {
super.initState();
// Load the animation file from the bundle, note that you could also
// download this. The RiveFile just expects a list of bytes.
rootBundle.load('assets/rocket.riv').then(
(data) async {
// Load the RiveFile from the binary data.
final file = RiveFile.import(data);
// The artboard is the root of the animation and gets drawn in the
// Rive widget.
final artboard = file.mainArtboard;
var controller =
StateMachineController.fromArtboard(artboard, 'Button');
if (controller != null) {
artboard.addController(controller);
_hoverInput = controller.findInput('Hover');
_pressInput = controller.findInput('Press');
}
setState(() => _riveArtboard = artboard);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: const Text('Button State Machine'),
),
body: Center(
child: _riveArtboard == null
? const SizedBox()
: MouseRegion(
onEnter: (_) => _hoverInput?.value = true,
onExit: (_) => _hoverInput?.value = false,
child: GestureDetector(
onTapDown: (_) => _pressInput?.value = true,
onTapCancel: () => _pressInput?.value = false,
onTapUp: (_) => _pressInput?.value = false,
child: SizedBox(
width: 250,
height: 250,
child: Rive(
artboard: _riveArtboard!,
),
),
),
),
),
);
}
}

The getter '_controller' was called on null. Flutter

I'm using this package in Flutter: https://pub.dev/packages/circular_countdown_timer
However, I need to create a separate widget to separate my code a little bit. I created something like this:
import 'package:circular_countdown_timer/circular_countdown_timer.dart';
import 'package:flutter/material.dart';
class CustomOtpCard extends StatefulWidget {
final String duration;
final String accountName;
final String otp;
final Function(CountDownController) onComplete;
CustomOtpCard({
#required this.duration,
#required this.accountName,
#required this.otp,
#required this.onComplete,
});
#override
_CustomOtpCardState createState() => _CustomOtpCardState();
}
class _CustomOtpCardState extends State<CustomOtpCard> {
CountDownController _controller = CountDownController();
#override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: CircularCountDownTimer(
duration: int.parse(widget.duration),
onComplete: widget.onComplete(_controller),
initialDuration: 0,
ringColor: Colors.grey[300],
fillColor: Colors.purple,
backgroundColor: Colors.purple[500],
width: 50.0,
height: 50.0,
),
title: Text(widget.accountName),
subtitle: Text(widget.otp),
),
);
}
}
Now, I use it like this:
return ListView.builder(
itemCount: _totpItems.length,
itemBuilder: (context, index) {
return CustomOtpCard(
duration: '30',
onComplete: (controller) {
BlocProvider.of<TotpGeneratorBloc>(context)
.add(GetTotpItemsEvent());
controller.restart();
},
accountName: _totpItems[index].accountName,
otp: _totpItems[index].otp,
);
},
);
Then the error shows up "The getter '_controller' was called on null.". Here's the image of my error:
It says, that the _controller I passed to the callback is null. What am I doing wrong? Can someone give me a hint?
Thanks!
I presume that when you call controller.restart(), _CustomOtpCardState has been disposed then _controller do not exist anymore.
You could do it this way :
First turn onComplete to a VoidCallback (equivalent to void Function()) :
final VoidCallback onComplete;
Then use it this way :
...
duration: int.parse(widget.duration),
onComplete: () {
widget.onComplete();
_controller.restart();
},
initialDuration: 0,
...
In your ListView.builder :
return CustomOtpCard(
duration: '30',
onComplete: () {
BlocProvider.of<TotpGeneratorBloc>(context)
.add(GetTotpItemsEvent());
},
accountName: _totpItems[index].accountName,
otp: _totpItems[index].otp,
);

Tap to focus for Flutter camera

A simple, but very complicated question: What’s the best way to add a tap to focus functionality for the Flutter camera?
I’ve searched the entire World Wide Web about elegant solutions, but I found nothing.
Do you have an idea?
I might be late but you can try adv_camera package.
Here is a simple example:
import 'package:adv_camera/adv_camera.dart';
import 'package:flutter/material.dart';
class CameraApp extends StatefulWidget {
final String id;
const CameraApp({Key? key, required this.id}) : super(key: key);
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
List<String> pictureSizes = <String>[];
String? imagePath;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AdvCamera Example'),
),
body: SafeArea(
child: AdvCamera(
initialCameraType: CameraType.rear,
onCameraCreated: _onCameraCreated,
onImageCaptured: (String path) {
if (this.mounted)
setState(() {
imagePath = path;
});
},
cameraPreviewRatio: CameraPreviewRatio.r16_9,
focusRectColor: Colors.purple,
focusRectSize: 200,
),
),
floatingActionButton: FloatingActionButton(
heroTag: "capture",
child: Icon(Icons.camera),
onPressed: () {
cameraController!.captureImage();
},
),
);
}
AdvCameraController? cameraController;
_onCameraCreated(AdvCameraController controller) {
this.cameraController = controller;
this.cameraController!.getPictureSizes().then((pictureSizes) {
setState(() {
this.pictureSizes = pictureSizes ?? <String>[];
});
});
}
}

What should I do if the nodes of a multlevel list have content that should be displayed when clicked? (Advanced ExpansionTile)

For a flutter project I needed a tree structure in which when clicking on a node, not only the entries below it are displayed, but - as with the file manager in Windows - also the content: On a smartphone as a new screen and on a tablet as an additional area to the right of the list.
Unfortunately, the standard ExpansionTile does not have this capability.
Since I'm a newcomer to Flutter, I first looked at the source code and tried to understand the most important parts (I'm still at it ;-). Then I made the following changes:
A property 'AlignOpener' now decides whether the open/close icon is displayed on the left or right.
I added the properties 'onTap' and 'onLongPress' as callback. Once one of these properties is assigned in the calling widget, the '_isInAdvancedMode' property is set to true and an IconButton is inserted as leading or trailing.
Clicking this button will open/close the directory tree. A click on the remaining part is forwarded to the calling widget via callback.
Finally, I added a property 'indentListTile' to control the indent of each layer.
If none of the properties are assigned, the AvancedExpansionTile behaves like the standard ExpansionTile.
Since I'm a newbie, there's still a lot of uncertainty as to whether the code is really correct. As far as I can see, it works, but I would be happy if experienced developers among you could check the code and make suggestions for improvements if necessary.
Maybe the code solves a problem that others also had?
Her is the code:
master_list_item.dart
import 'package:meta/meta.dart';
class ItemMaster {
ItemMaster(
{this.id,
#required this.title,
this.subtitle,
this.children = const <ItemMaster>[]});
final int id;
final String title;
final String subtitle;
final List<ItemMaster> children;
}
master_list.dart
import 'master_list_item.dart';
class MasterList {
List<ItemMaster> get items {
return _items;
}
final List<ItemMaster> _items = <ItemMaster>[
ItemMaster(
title: 'Chapter Master List',
id: 1,
children: <ItemMaster>[
ItemMaster(
title: 'Scene A0',
id: 2,
children: <ItemMaster>[
ItemMaster(title: 'Item A0.1', id: 3),
ItemMaster(title: 'Item A0.2', id: 4),
ItemMaster(title: 'Item A0.3', id: 5),
],
),
ItemMaster(title: 'Scene A1', id: 6),
ItemMaster(title: 'Scene A2', id: 7),
],
),
ItemMaster(
title: 'Chapter B',
id: 8,
children: <ItemMaster>[
ItemMaster(title: 'Scene B0', id: 9),
ItemMaster(title: 'Scene B1', id: 10),
],
),
ItemMaster(
title: 'Chapter C',
id: 11,
children: <ItemMaster>[
ItemMaster(title: 'Scene C0', id: 12),
ItemMaster(title: 'Scene C1', id: 13),
ItemMaster(
title: 'Scene C2',
id: 14,
children: <ItemMaster>[
ItemMaster(title: 'Item C2.0', id: 15),
ItemMaster(title: 'Item C2.1', id: 16),
ItemMaster(title: 'Item C2.2', id: 17),
ItemMaster(title: 'Item C2.3', id: 18),
],
),
],
),
];
}
main.dart
import 'package:flutter/material.dart';
import 'advanced_expansion_tile.dart';
import 'master_list_item.dart';
import 'master_list.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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<ItemMaster> items;
#override
void initState() {
// TODO: implement initState
super.initState();
items = MasterList().items;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, int index) => MasterListEntry(items[index]),
)),
),
);
}
}
class MasterListEntry extends StatelessWidget {
const MasterListEntry(this.entry);
final ItemMaster entry;
Widget _buildTiles(ItemMaster root) {
if (root.children.isEmpty)
return ListTile(
title: Text(root.title),
onTap: () => print("onTap listTile"),
);
return AdvancedExpansionTile(
key: PageStorageKey<ItemMaster>(root),
title: Text(root.title),
children: root.children.map(_buildTiles).toList(),
onTap: () => print("onTap AdvancedExpansionTile"),
alignOpener: AlignOpener.Right,
indentListTile: 15.0,
// isInAdvancedMode: true,
);
}
#override
Widget build(BuildContext context) {
return _buildTiles(entry);
}
}
advanced_expansion_tile.dart (based on the source code from the Flutter team)
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
const Duration _kExpand = Duration(milliseconds: 200);
enum AlignOpener { Left, Right }
class AdvancedExpansionTile extends StatefulWidget {
const AdvancedExpansionTile({
Key key,
this.leading,
this.trailing,
#required this.title,
this.backgroundColor,
this.onExpansionChanged,
this.onTap,
this.onLongPress,
this.alignOpener,
this.indentListTile,
this.children = const <Widget>[],
this.initiallyExpanded = false,
}) : assert(initiallyExpanded != null),
super(key: key);
/// A widget to display before the title.
///
/// Typically a [CircleAvatar] widget.
final Widget leading;
/// The primary content of the list item.
///
/// Typically a [Text] widget.
final Widget title;
/// Called when the tile expands or collapses.
///
/// When the tile starts expanding, this function is called with the value
/// true. When the tile starts collapsing, this function is called with
/// the value false.
final ValueChanged<bool> onExpansionChanged;
/// The widgets that are displayed when the tile expands.
///
/// Typically [ListTile] widgets.
final List<Widget> children;
/// The color to display behind the sublist when expanded.
final Color backgroundColor;
/// Specifies if the list tile is initially expanded (true) or collapsed (false, the default).
final bool initiallyExpanded;
/// A widget to display instead of a rotating arrow icon.
final Widget trailing;
/// A callback for onTap and onLongPress on the listTile
final GestureTapCallback onTap;
final GestureLongPressCallback onLongPress;
/// The side where the Open/Close-Icon/IconButton will be placed
final AlignOpener alignOpener;
/// indent of listTile (left)
final indentListTile;
#override
_AdvancedExpansionTileState createState() => _AdvancedExpansionTileState();
}
class _AdvancedExpansionTileState extends State<AdvancedExpansionTile>
with SingleTickerProviderStateMixin {
static final Animatable<double> _easeOutTween =
CurveTween(curve: Curves.easeOut);
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0.0, end: 0.5);
final ColorTween _borderColorTween = ColorTween();
final ColorTween _headerColorTween = ColorTween();
final ColorTween _iconColorTween = ColorTween();
final ColorTween _backgroundColorTween = ColorTween();
AnimationController _controller;
Animation<double> _iconTurns;
Animation<double> _heightFactor;
Animation<Color> _borderColor;
Animation<Color> _headerColor;
Animation<Color> _iconColor;
Animation<Color> _backgroundColor;
bool _isExpanded = false;
/// If set to true an IconButton will be created. This button will open/close the children
bool _isInAdvancedMode;
AlignOpener _alignOpener;
double _indentListTile;
#override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
_heightFactor = _controller.drive(_easeInTween);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
_borderColor = _controller.drive(_borderColorTween.chain(_easeOutTween));
_headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
_iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
_backgroundColor =
_controller.drive(_backgroundColorTween.chain(_easeOutTween));
_isExpanded =
PageStorage.of(context)?.readState(context) ?? widget.initiallyExpanded;
if (_isExpanded) _controller.value = 1.0;
/// OnTap or onLongPress are handled in the calling widget --> AdvancedExpansionTile is in Advanced Mode
if (widget.onTap != null || widget.onLongPress != null) {
_isInAdvancedMode = true;
} else {
_isInAdvancedMode = false;
}
/// fallback to standard behaviour if aligning isn't set
_alignOpener = widget.alignOpener ?? AlignOpener.Right;
/// if no indent is set the indent will be 0.0
_indentListTile = widget.indentListTile ?? 0.0;
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void _handleTap() {
setState(() {
_isExpanded = !_isExpanded;
if (_isExpanded) {
_controller.forward();
} else {
_controller.reverse().then<void>((void value) {
if (!mounted) return;
setState(() {
// Rebuild without widget.children.
});
});
}
PageStorage.of(context)?.writeState(context, _isExpanded);
});
if (widget.onExpansionChanged != null)
widget.onExpansionChanged(_isExpanded);
}
Widget _buildChildren(BuildContext context, Widget child) {
final Color borderSideColor = _borderColor.value ?? Colors.transparent;
return Container(
decoration: BoxDecoration(
color: _backgroundColor.value ?? Colors.transparent,
border: Border(
top: BorderSide(color: borderSideColor),
bottom: BorderSide(color: borderSideColor),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTileTheme.merge(
iconColor: _iconColor.value,
textColor: _headerColor.value,
child: ListTile(
onTap: () {
_isInAdvancedMode ? widget.onTap() : _handleTap();
}, // in AdvancedMode a callback will handle the gesture inside the calling widget
onLongPress: () {
_isInAdvancedMode ? widget.onLongPress() : _handleTap();
}, // in AdvancedMode a callback will handle the gesture inside the calling widget
leading: getLeading(),
title: widget.title,
trailing: getTrailing(),
),
),
ClipRect(
child: Padding(
padding: EdgeInsets.only(left: _indentListTile), // set the indent
child: Align(
heightFactor: _heightFactor.value,
child: child,
),
)),
],
),
);
}
#override
void didChangeDependencies() {
final ThemeData theme = Theme.of(context);
_borderColorTween..end = theme.dividerColor;
_headerColorTween
..begin = theme.textTheme.subhead.color
..end = theme.accentColor;
_iconColorTween
..begin = theme.unselectedWidgetColor
..end = theme.accentColor;
_backgroundColorTween..end = widget.backgroundColor;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
final bool closed = !_isExpanded && _controller.isDismissed;
return AnimatedBuilder(
animation: _controller.view,
builder: _buildChildren,
child: closed ? null : Column(children: widget.children),
);
}
/// A method to decide what will be shown in the leading part of the lisTile
getLeading() {
if (_alignOpener.toString() == AlignOpener.Left.toString() &&
_isInAdvancedMode == true) {
return buildIcon(); //IconButton will be created
} else if (_alignOpener.toString() == AlignOpener.Left.toString() &&
_isInAdvancedMode == false) {
return widget.leading ??
RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
);
} else {
return widget.leading;
}
}
/// A method to decide what will be shown in the trailing part of the lisTile
getTrailing() {
if (_alignOpener.toString() == AlignOpener.Right.toString() &&
_isInAdvancedMode == true) {
return buildIcon(); //IconButton will be created
} else if (_alignOpener.toString() == AlignOpener.Right.toString() &&
_isInAdvancedMode == false) {
return widget.trailing ??
RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
);
} else {
return widget.leading;
}
}
/// A widget to build the IconButton for the leading or trailing part of the listTile
Widget buildIcon() {
return Container(
child: RotationTransition(
turns: _iconTurns,
child: IconButton(
icon: Icon(Icons.expand_more),
onPressed: () {
_handleTap();
//toDo: open/close is working but not the animation
},
),
));
}
}