How to collapse the ExpansionTile on button click? – Flutter

0

Issue

I’m opening up a form to enter the details and close it upon filling everything and submitting it, but only the trailing icon is getting changed, also I’m trying to set the changes in the onExpansionChanged but not reflecting in the UI.

Updated Code

Scaffold(
        backgroundColor: Colors.grey.shade200,
        appBar: AppBar(
          title: TextButton(
            child: Text(count.toString()),
            onPressed: () {
              Navigator.push(context,
                  MaterialPageRoute(builder: (_) => RegisterLetterBookedListScreen()));
            },
          ),
          backgroundColor: ColorConstants.kPrimaryColor,
          elevation: 0,
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            MotionToast.success(
              title: 'Amount to be Collected',
              titleStyle: TextStyle(fontWeight: FontWeight.bold),
              description: '\u{20B9} $amountToBeCollected',
              descriptionStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
              layoutOrientation: ORIENTATION.LTR,
              animationType: ANIMATION.FROM_LEFT,
              width: 300,
            ).show(context);
          },
          child: Text(
            '\u{20B9} $amountToBeCollected',
            style: TextStyle(fontSize: 20),
          ),
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
        ),
        body: Padding(
          padding: const EdgeInsets.all(20.0),
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                //Article Details
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    

                //Sender Details
                ExpansionTile(
                  key: GlobalKey(),
                  trailing: (senderExpansion == false)
                      ? Icon(
                          MdiIcons.toggleSwitchOffOutline,
                          size: 40,
                          color: Colors.black54,
                        )
                      : Icon(MdiIcons.toggleSwitchOutline, size: 40, color: Colors.red),
                  onExpansionChanged: (value) {
                      senderExpansion = value;
                  },
                  maintainState: true,
                  initiallyExpanded: senderExpansion,
                  title: Text(
                    'Sender Details',
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        color: senderHeadingColor,
                        fontSize: 20,
                        letterSpacing: 1),
                  ),
                  children: [
                    Column(
                      children: [
                        CInputForm(
                          readOnly: false,
                          iconData: Icons.person,
                          labelText: 'Name *',
                          controller: senderNameController,
                          textType: TextInputType.text,
                          typeValue: 'Name',
                          focusNode: senderNameFocusNode,
                        ),
                        CInputForm(
                          readOnly: false,
                          iconData: MdiIcons.home,
                          labelText: 'Address *',
                          controller: senderAddressController,
                          textType: TextInputType.multiline,
                          typeValue: 'Address',
                          focusNode: senderAddressFocusNode,
                        ),
                        Card(
                          elevation: 0,
                          child: TypeAheadFormField(
                            textFieldConfiguration: TextFieldConfiguration(
                                style: TextStyle(color: Colors.blueGrey),
                                controller: senderPinCodeCityController,
                                autofocus: false,
                                decoration: InputDecoration(
                                    prefixIcon: Icon(
                                      Icons.location_on_outlined,
                                      color: Colors.blueGrey,
                                    ),
                                    fillColor: Colors.white,
                                    filled: true,
                                    enabledBorder: OutlineInputBorder(
                                      borderSide: BorderSide(color: Colors.white),
                                    ),
                                    labelText: 'Pincode/Office Name *',
                                    labelStyle: TextStyle(
                                        color: ColorConstants.kAmberAccentColor),
                                    border: OutlineInputBorder(
                                        borderSide: BorderSide(color: Colors.white)))),
                            onSuggestionSelected:
                                (Map<String, String> suggestion) async {
                              senderPinCodeCityController.text = suggestion['pinCode']!;
                              senderCityController.text = suggestion['city']!;
                              senderStateController.text = suggestion['state']!;
                            },
                            itemBuilder: (context, Map<String, String> suggestion) {
                              return ListTile(
                                title: Text(suggestion['officeName']! +
                                    ", " +
                                    suggestion['pinCode']!),
                              );
                            },
                            suggestionsCallback: (pattern) async {
                              return await FetchPin.getSuggestions(pattern);
                            },
                            validator: (value) {
                              if (value!.isEmpty) {
                                issPin = false;
                                isLoading = false;
                              }
                            },
                          ),
                        ),
                        Visibility(
                          visible: issPin == false ? senderPinCodeCityController.text.isEmpty ? true : false : false,
                          child: Align(
                              alignment: Alignment.topLeft,
                              child: Padding(
                                padding: const EdgeInsets.only(left: 17.0),
                                child: Text('Select a Pincode/Office name', style: TextStyle(fontSize: 10, color: ColorConstants.kPrimaryColor),),
                              )),
                        ),
                        Visibility(
                          visible:
                              senderPinCodeCityController.text.isEmpty ? false : true,
                          child: CInputForm(
                            readOnly: true,
                            iconData: Icons.location_city,
                            labelText: 'City',
                            controller: senderCityController,
                            textType: TextInputType.text,
                            typeValue: 'City',
                            focusNode: senderPinCodeFocusNode,
                          ),
                        ),
                        Visibility(
                          visible:
                          senderPinCodeCityController.text.isEmpty ? false : true,
                          child: CInputForm(
                            readOnly: true,
                            iconData: Icons.location_city,
                            labelText: 'State',
                            controller: senderStateController,
                            textType: TextInputType.text,
                            typeValue: 'City',
                            focusNode: senderPinCodeFocusNode,
                          ),
                        ),
                        CInputForm(
                          readOnly: false,
                          iconData: MdiIcons.cellphone,
                          labelText: 'Mobile Number',
                          controller: senderMobileNumberController,
                          textType: TextInputType.number,
                          typeValue: 'MobileNumber',
                          focusNode: senderMobileFocusNode,
                        ),
                        CInputForm(
                          readOnly: false,
                          iconData: MdiIcons.email,
                          labelText: 'Email',
                          controller: senderEmailController,
                          textType: TextInputType.emailAddress,
                          typeValue: 'Email',
                          focusNode: senderEmailFocusNode,
                        ),
                      ],
                    )
                  ],
                ),

                //Addressee Details
                ExpansionTile(
                  trailing: (addresseeExpansion == false)
                      ? Icon(
                          MdiIcons.toggleSwitchOffOutline,
                          size: 40,
                          color: Colors.black54,
                        )
                      : Icon(MdiIcons.toggleSwitchOutline, size: 40, color: Colors.red),
                  onExpansionChanged: (value) {
                      addresseeExpansion = value;
                  },
                  maintainState: true,
                  title: Text('Addressee Details',
                      style: TextStyle(
                          fontWeight: FontWeight.bold,
                          color: addresseeHeadingColor,
                          fontSize: 20,
                          letterSpacing: 1)),
                  children: [
                    Column(
                      children: [
                        CInputForm(
                          readOnly: false,
                          iconData: Icons.person,
                          labelText: 'Name *',
                          controller: addresseeNameController,
                          textType: TextInputType.text,
                          typeValue: 'Name',
                          focusNode: addresseeNameFocusNode,
                        ),
                        CInputForm(
                          readOnly: false,
                          iconData: MdiIcons.home,
                          labelText: 'Address *',
                          controller: addresseeAddressController,
                          textType: TextInputType.multiline,
                          typeValue: 'Address',
                          focusNode: addresseeAddressFocusNode,
                        ),
                        Card(
                          elevation: 0,
                          child: TypeAheadFormField(
                            textFieldConfiguration: TextFieldConfiguration(
                                style: TextStyle(color: Colors.blueGrey),
                                controller: addresseePinCodeCityController,
                                autofocus: false,
                                decoration: InputDecoration(
                                    prefixIcon: Icon(
                                      Icons.location_on_outlined,
                                      color: Colors.blueGrey,
                                    ),
                                    fillColor: Colors.white,
                                    filled: true,
                                    enabledBorder: OutlineInputBorder(
                                      borderSide: BorderSide(color: Colors.white),
                                    ),
                                    labelText: 'Pincode/Office Name *',
                                    labelStyle: TextStyle(
                                        color: ColorConstants.kAmberAccentColor),
                                    border: OutlineInputBorder(
                                        borderSide: BorderSide(color: Colors.white)))),
                            onSuggestionSelected:
                                (Map<String, String> suggestion) async {
                              addresseePinCodeCityController.text =
                                  suggestion['pinCode']!;
                              addresseeCityController.text = suggestion['city']!;
                              addresseeStateController.text = suggestion['state']!;
                            },
                            itemBuilder: (context, Map<String, String> suggestion) {
                              return ListTile(
                                title: Text(suggestion['officeName']! +
                                    ", " +
                                    suggestion['pinCode']!),
                              );
                            },
                            suggestionsCallback: (pattern) async {
                              return await FetchPin.getSuggestions(pattern);
                            },
                            validator: (value) {
                              if (value!.isEmpty) {
                                isLoading = false;
                                isaPin = false;

                              }
                            },
                          ),
                        ),
                        Visibility(
                          visible: isaPin == false ? addresseePinCodeCityController.text.isEmpty ? true : false : false,
                          child: Align(
                              alignment: Alignment.topLeft,
                              child: Padding(
                                padding: const EdgeInsets.only(left: 17.0),
                                child: Text('Select a Pincode/Office name', style: TextStyle(fontSize: 10, color: ColorConstants.kPrimaryColor),),
                              )),
                        ),
                        Visibility(
                          visible: addresseePinCodeCityController.text.isEmpty
                              ? false
                              : true,
                          child: CInputForm(
                            readOnly: true,
                            iconData: Icons.location_city,
                            labelText: 'City',
                            controller: addresseeCityController,
                            textType: TextInputType.text,
                            typeValue: 'City',
                            focusNode: addresseePinCodeFocusNode,
                          ),
                        ),
                        Visibility(
                          visible: addresseePinCodeCityController.text.isEmpty
                              ? false
                              : true,
                          child: CInputForm(
                            readOnly: true,
                            iconData: Icons.location_city,
                            labelText: 'State',
                            controller: addresseeStateController,
                            textType: TextInputType.text,
                            typeValue: 'State',
                            focusNode: addresseePinCodeFocusNode,
                          ),
                        ),
                        CInputForm(
                          readOnly: false,
                          iconData: MdiIcons.cellphone,
                          labelText: 'Mobile Number',
                          controller: addresseeMobileNumberController,
                          textType: TextInputType.number,
                          typeValue: 'MobileNumber',
                          focusNode: addresseeMobileFocusNode,
                        ),
                        CInputForm(
                          readOnly: false,
                          iconData: MdiIcons.email,
                          labelText: 'Email',
                          controller: addresseeEmailController,
                          textType: TextInputType.emailAddress,
                          typeValue: 'Email',
                          focusNode: addresseeEmailFocusNode,
                        ),
                      ],
                    ),
                  ],
                ),

                //Submit
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Center(
                    child: ElevatedButton(
                      style: ButtonStyle(
                          backgroundColor:
                              MaterialStateProperty.all<Color>(Colors.white),
                          shape: MaterialStateProperty.all<RoundedRectangleBorder>(
                              RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(10),
                                  side: BorderSide(color: Colors.blueGrey)))),
                      onPressed: () async {
                        setState(() {
                          senderExpansion = !senderExpansion;
                          addresseeExpansion = !addresseeExpansion;
                        });
                        final ifPresent = await RegisterLetterTable()
                            .select()
                            .ArticleNumber
                            .equals(articleNumberController.text)
                            .toMapList();
                        if (ifPresent.isNotEmpty) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                            content: Text('Article already scanned'),
                            behavior: SnackBarBehavior.floating,
                          ));
                        } else {
                          if (articleNumberController.text.isEmpty) {
                            setState(() {
                              articleNumberFocusNode.requestFocus();
                            });
                          } else if (weightController.text.isEmpty) {
                            setState(() {
                              weightFocusNode.requestFocus();
                            });
                          } else if (insuranceCheck == true) {
                            if (insuranceController.text.isEmpty)
                              insuranceFocusNode.requestFocus();
                          } else if (valuePayableCheck == true) {
                            if (valuePayablePostController.text.isEmpty)
                              valuePayableFocusNode.requestFocus();
                          }

                          if (senderNameController.text.isEmpty ||
                              senderAddressController.text.isEmpty ||
                              senderPinCodeCityController.text.isEmpty) {
                            setState(() {
                              senderExpansion = true;
                            });
                            if (addresseeNameController.text.isEmpty ||
                                addresseeAddressController.text.isEmpty ||
                                addresseePinCodeCityController.text.isEmpty) {
                              setState(() {
                                addresseeExpansion = true;
                              });
                            } else
                              setState(() {
                                addresseeExpansion = false;
                              });
                          } else
                            senderExpansion = false;
                          if (addresseeNameController.text.isEmpty ||
                              addresseeAddressController.text.isEmpty ||
                              addresseePinCodeCityController.text.isEmpty) {
                            setState(() {
                              addresseeExpansion = true;
                            });
                          } else
                            setState(() {
                              addresseeExpansion = false;
                            });

                          if (formGlobalKey.currentState!.validate()) {
                            formGlobalKey.currentState!.save();

                            showDialog<void>(
                                context: context,
                                barrierDismissible: false,
                                builder: (BuildContext context) {
                                  return ConformationDialog(
                                      articleNumber: articleNumberController.text,
                                      weight: weightController.text,
                                      prepaidAmount: prepaidAmountController.text,
                                      acknowledgement: acknowledgementCheck,
                                      insurance: insuranceCheck == true
                                          ? insuranceController.text
                                          : '',
                                      valuePayablePost: valuePayableCheck == true
                                          ? valuePayablePostController.text
                                          : '',
                                      amountToBeCollected:
                                          amountToBeCollectedController.text,
                                      senderName: senderNameController.text,
                                      senderAddress: senderAddressController.text,
                                      senderPinCode: senderPinCodeCityController.text,
                                      senderCity: senderCityController.text,
                                      senderState: senderStateController.text,
                                      senderMobileNumber:
                                          senderMobileNumberController.text,
                                      senderEmail: senderEmailController.text,
                                      addresseeName: addresseeNameController.text,
                                      addresseeAddress: addresseeAddressController.text,
                                      addresseePinCode:
                                          addresseePinCodeCityController.text,
                                      addresseeCity: addresseeCityController.text,
                                      addresseeState: addresseeStateController.text,
                                      addresseeMobileNumber:
                                          addresseeMobileNumberController.text,
                                      addresseeEmail: addresseeEmailController.text,
                                      function: () {
                                        Navigator.of(context).pop();
                                        printFunction();
                                      });
                                });
                          }
                        }
                      },
                      child: Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: new Text(
                          'SUBMIT',
                          overflow: TextOverflow.ellipsis,
                          style: TextStyle(color: ColorConstants.kAmberAccentColor),
                        ),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        ))

Solution

Since your using Multiple ExpansionTiles, as said by @Jai Techie you need a key to control its state, but as per my knowledge we cannot provide the state of the ExpansionTile as the GlobalKey, so in order to acheive it create a new CustomExpansionTile.

import 'package:flutter/material.dart';


class FormExpansionTileCard extends StatefulWidget {

  const FormExpansionTileCard({
    Key? key,
    this.leading,
    required this.title,
    this.subtitle,
    this.onExpansionChanged,
    this.children = const <Widget>[],
    this.trailing,
    this.borderRadius = const BorderRadius.all(Radius.circular(8.0)),
    this.elevation = 2.0,
    this.initialElevation = 0.0,
    this.initiallyExpanded = false,
    this.initialPadding = EdgeInsets.zero,
    this.finalPadding = const EdgeInsets.only(bottom: 6.0),
    this.contentPadding,
    this.baseColor,
    this.expandedColor,
    this.expandedTextColor,
    this.duration = const Duration(milliseconds: 200),
    this.elevationCurve = Curves.easeOut,
    this.heightFactorCurve = Curves.easeIn,
    this.turnsCurve = Curves.easeIn,
    this.colorCurve = Curves.easeIn,
    this.paddingCurve = Curves.easeIn,
    this.isThreeLine = false,
    this.shadowColor = const Color(0xffaaaaaa),
    this.animateTrailing = false,
  })  : assert(initiallyExpanded != null),
        super(key: key);

  final bool isThreeLine;
  final Widget? leading;
  final Widget title;
  final Widget? subtitle;
  final ValueChanged<bool>? onExpansionChanged;
  final List<Widget> children;
  final Widget? trailing;
  final bool animateTrailing;
  final BorderRadiusGeometry borderRadius;
  final double elevation;
  final double initialElevation;
  final Color shadowColor;
  final bool initiallyExpanded;
  final EdgeInsetsGeometry initialPadding;
  final EdgeInsetsGeometry finalPadding;
  final EdgeInsetsGeometry? contentPadding;
  final Color? baseColor;
  final Color? expandedColor;
  final Color? expandedTextColor;
  final Duration duration;
  final Curve elevationCurve;
  final Curve heightFactorCurve;
  final Curve turnsCurve;
  final Curve colorCurve;
  final Curve paddingCurve;

  @override
  FormExpansionTileCardState createState() => FormExpansionTileCardState();
}

class FormExpansionTileCardState extends State<FormExpansionTileCard>
    with SingleTickerProviderStateMixin {
  static final Animatable<double> _halfTween =
  Tween<double>(begin: 0.0, end: 0.5);

  final ColorTween _headerColorTween = ColorTween();
  final ColorTween _iconColorTween = ColorTween();
  final ColorTween _materialColorTween = ColorTween();
  late EdgeInsetsTween _edgeInsetsTween;
  late Animatable<double> _elevationTween;
  late Animatable<double> _heightFactorTween;
  late Animatable<double> _turnsTween;
  late Animatable<double> _colorTween;
  late Animatable<double> _paddingTween;

  late AnimationController _controller;
  late Animation<double> _iconTurns;
  late Animation<double> _heightFactor;
  late Animation<double> _elevation;
  late Animation<Color?> _headerColor;
  late Animation<Color?> _iconColor;
  late Animation<Color?> _materialColor;
  late Animation<EdgeInsets> _padding;

  bool _isExpanded = false;

  @override
  void initState() {
    super.initState();
    _edgeInsetsTween = EdgeInsetsTween(
      begin: widget.initialPadding as EdgeInsets?,
      end: widget.finalPadding as EdgeInsets?,
    );
    _elevationTween = CurveTween(curve: widget.elevationCurve);
    _heightFactorTween = CurveTween(curve: widget.heightFactorCurve);
    _colorTween = CurveTween(curve: widget.colorCurve);
    _turnsTween = CurveTween(curve: widget.turnsCurve);
    _paddingTween = CurveTween(curve: widget.paddingCurve);

    _controller = AnimationController(duration: widget.duration, vsync: this);
    _heightFactor = _controller.drive(_heightFactorTween);
    _iconTurns = _controller.drive(_halfTween.chain(_turnsTween));
    _headerColor = _controller.drive(_headerColorTween.chain(_colorTween));
    _materialColor = _controller.drive(_materialColorTween.chain(_colorTween));
    _iconColor = _controller.drive(_iconColorTween.chain(_colorTween));
    _elevation = _controller.drive(
        Tween<double>(begin: widget.initialElevation, end: widget.elevation)
            .chain(_elevationTween));
    _padding = _controller.drive(_edgeInsetsTween.chain(_paddingTween));
    _isExpanded = PageStorage.of(context)?.readState(context) as bool? ??
        widget.initiallyExpanded;
    if (_isExpanded) _controller.value = 1.0;
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  // Credit: Simon Lightfoot - https://stackoverflow.com/a/48935106/955974
  void _setExpansion(bool shouldBeExpanded) {
    if (shouldBeExpanded != _isExpanded) {
      setState(() {
        _isExpanded = shouldBeExpanded;
        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);
    }
  }

  void expand() {
    _setExpansion(true);
  }

  void collapse() {
    _setExpansion(false);
  }

  void toggleExpansion() {
    _setExpansion(!_isExpanded);
  }

  Widget _buildChildren(BuildContext context, Widget? child) {
    return Padding(
      padding: _padding.value,
      child: Material(
        color: Colors.grey[200],
        borderRadius: widget.borderRadius,
        elevation: 0,
        shadowColor: widget.shadowColor,
        child: Container(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              InkWell(
                customBorder:
                RoundedRectangleBorder(borderRadius: widget.borderRadius),
                onTap: toggleExpansion,
                child: ListTileTheme.merge(
                  iconColor: _iconColor.value,
                  textColor: _headerColor.value,
                  child: Padding(
                    padding: const EdgeInsets.all(2.0),
                    child: ListTile(
                      isThreeLine: widget.isThreeLine,
                      contentPadding: widget.contentPadding,
                      leading: widget.leading,
                      title: widget.title,
                      subtitle: widget.subtitle,
                      trailing: RotationTransition(
                        turns: widget.trailing == null || widget.animateTrailing
                            ? _iconTurns
                            : AlwaysStoppedAnimation(0),
                        child: widget.trailing ?? Icon(Icons.expand_more),
                      ),
                    ),
                  ),
                ),
              ),
              ClipRect(
                child: Align(
                  heightFactor: _heightFactor.value,
                  child: child,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void didChangeDependencies() {
    final ThemeData theme = Theme.of(context);
    _headerColorTween
      ..begin = theme.textTheme.subtitle1!.color
      ..end = widget.expandedTextColor ?? theme.accentColor;
    _iconColorTween
      ..begin = theme.unselectedWidgetColor
      ..end = widget.expandedTextColor ?? theme.accentColor;
    _materialColorTween
      ..begin = widget.baseColor ?? theme.canvasColor
      ..end = widget.expandedColor ?? theme.cardColor;
    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),
    );
  }
}

Initialize the key value in your dart file

  final GlobalKey<FormExpansionTileCardState> cardA = new GlobalKey();
  final GlobalKey<FormExpansionTileCardState> cardB = new GlobalKey();
                //Sender Details
                FormExpansionTileCard(
                  key: cardA,
                  trailing: (senderExpansion == false)
                      ? Icon(
                          MdiIcons.toggleSwitchOffOutline,
                          size: 40,
                          color: Colors.black54,
                        )
                      : Icon(MdiIcons.toggleSwitchOutline, size: 40, color: Colors.red),
                  onExpansionChanged: (value) {
                      senderExpansion = value;
                  },
                  initiallyExpanded: senderExpansion,
                  title: Text(
                    'Sender Details',
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        color: senderHeadingColor,
                        fontSize: 20,
                        letterSpacing: 1),
                  ),
                  children: [
                    //...Your Widgets
                  ],
                ),
                //Addressee Details
                FormExpansionTileCard(
                  key: cardB,
                  trailing: (senderExpansion == false)
                      ? Icon(
                          MdiIcons.toggleSwitchOffOutline,
                          size: 40,
                          color: Colors.black54,
                        )
                      : Icon(MdiIcons.toggleSwitchOutline, size: 40, color: Colors.red),
                  onExpansionChanged: (value) {
                      addresseeExpansion = value;
                  },
                  initiallyExpanded: addresseeExpansion,
                  title: Text(
                    'AddresseeDetails',
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        color: senderHeadingColor,
                        fontSize: 20,
                        letterSpacing: 1),
                  ),
                  children: [
                    //...Your Widgets
                  ],
                ),

Answered By – Niroop Nife

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave A Reply

Your email address will not be published.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More