Search code examples
flutterdartstateful

How can I return value from stateful widget in flutter


I want to get value of assetImage[index] String that I stop on it and return it to store value in class that I made

This is code of widget that i wanna return new value of assetImage[index]

class BodyFormStyle extends StatefulWidget {
  BodyFormStyle(
      {super.key,
      required this.assetsImages,
      required this.indicatorText,
      required this.activeIndex});
  List<String> assetsImages;
  String indicatorText;
  int activeIndex;

  @override
  State<BodyFormStyle> createState() => _BodyFormStyleState();
}
class _BodyFormStyleState extends State<BodyFormStyle> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        CarouselSlider.builder(
          options: CarouselOptions(
              height: 30.h,
              initialPage: widget.activeIndex,
              enlargeCenterPage: true,
              enlargeStrategy: CenterPageEnlargeStrategy.height,
              enableInfiniteScroll: false,
              onPageChanged: (index, reason) {
                setState(() {
                  widget.activeIndex = index;
                });
              }),
          itemCount: widget.assetsImages.length,
          itemBuilder: (context, index, realIndex) {
            final assetsImage = widget.assetsImages[index];
            return buildImage(assetsImage, index);
          },
        ),
        buildIndicator(widget.indicatorText),
      ],
    );
  }

  Widget buildImage(String assetsImage, int index) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: screenSize.width * 0.05),
      decoration: BoxDecoration(
        border: Border.all(
          width: screenSize.width * 0.01,
          color: primaryColorStyle,
        ),
        borderRadius: BorderRadius.all(
          Radius.circular(screenSize.width * 0.07),
        ),
        image: DecorationImage(
          image: AssetImage(
            assetsImage,
          ),
          fit: BoxFit.cover,
        ),
      ),
    );
  }

  Widget buildIndicator(String indicatorText) {
    return Container(
      padding: EdgeInsets.fromLTRB(screenSize.width * 0.1,
          screenSize.height * 0.08, screenSize.width * 0.1, 0),
      child: SfSliderTheme(
        data: SfSliderThemeData(
          activeTrackColor: primaryColorStyle,
          inactiveTrackColor: secondaryColorStyle,
          tooltipBackgroundColor: primaryColorStyle,
          tooltipTextStyle: TextStyle(
            fontSize: screenSize.width * 0.04,
            fontWeight: FontWeight.bold,
          ),
        ),
        child: SfSlider(
          min: 0,
          max: widget.assetsImages.length - 1,
          interval: 1,
          value: widget.activeIndex,
          stepSize: 1,
          showDividers: true,
          enableTooltip: true,
          shouldAlwaysShowTooltip: true,
          tooltipTextFormatterCallback:
              (dynamic activeIndex, String formattedText) {
            return formattedText = indicatorText;
          },
          dividerShape: _DividerShape(),
          tooltipShape: const SfPaddleTooltipShape(),
          onChanged: (index) {
            setState(() {
              widget.activeIndex = index;
            });
          },
        ),
      ),
    );
  }
}
class _DividerShape extends SfDividerShape {
  @override
  void paint(PaintingContext context, Offset center, Offset? thumbCenter,
      Offset? startThumbCenter, Offset? endThumbCenter,
      {required RenderBox parentBox,
      required SfSliderThemeData themeData,
      SfRangeValues? currentValues,
      dynamic currentValue,
      required Paint? paint,
      required Animation<double> enableAnimation,
      required TextDirection textDirection}) {
    bool isActive;

    switch (textDirection) {
      case TextDirection.ltr:
        isActive = center.dx <= thumbCenter!.dx;
        break;
      case TextDirection.rtl:
        isActive = center.dx >= thumbCenter!.dx;
        break;
    }

    context.canvas.drawRect(
        Rect.fromCenter(center: center, width: 5.0, height: 10.0),
        Paint()
          ..isAntiAlias = true
          ..style = PaintingStyle.fill
          ..color = isActive ? themeData.activeTrackColor! : Colors.white);
  }
}

This is main page

import 'package:de_fit/presentations/constants.dart';
import 'package:de_fit/presentations/questions_page/desired_body_shape_page.dart';
import 'package:de_fit/sizes.dart';
import 'package:flutter/material.dart';
import 'package:de_fit/presentations/useful_widget.dart';

class CurrentBodyShapePage extends StatefulWidget {
  CurrentBodyShapePage({Key? key, required this.user}) : super(key: key);
  UserFormData user;

  @override
  State<CurrentBodyShapePage> createState() => _CurrentBodyShapePageState();
}

class _CurrentBodyShapePageState extends State<CurrentBodyShapePage> {
  final maleAssetsImages = [
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/male/maleshape1.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/male/maleshape2.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/male/maleshape3.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/male/maleshape4.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/male/maleshape5.jpg',
  ];
  final femaleAssetsImages = [
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/female/femaleshape1.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/female/femaleshape2.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/female/femaleshape3.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/female/femaleshape4.jpg',
    '/Users/hashimsidam/Projects/Flutter_Projects/de-fit/assets/images/body_shape/female/femaleshape5.jpg',
  ];
  int activeIndex = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: false,
        elevation: 0,
        bottom: PreferredSize(
          preferredSize: Size.fromHeight(
            spaceSize[deviceType][height][space[1]],
          ),
          child: StepLineStyle(
            activeStep: 2,
            subActiveStep: 1,
          ),
        ),
      ),
      body: Padding(
        padding: EdgeInsets.fromLTRB(
          0,
          spaceSize[deviceType][height][space[2]],
          0,
          spaceSize[deviceType][height][space[5]],
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              'ما هو شكل جسمك الحالي؟',
              style: TextStyle(
                  fontSize: textSize[deviceType][mainText[2]],
                  color: primaryColorStyle,
                  fontWeight: FontWeight.bold),
            ),
            BodyFormStyle(
              assetsImages: widget.user.gender == "Male"
                  ? maleAssetsImages
                  : femaleAssetsImages,
              indicatorText: 'الشكل الحالي',
              activeIndex: activeIndex,
            ),
            StartButton(
              height: buttonSize[deviceType][button[0]][height],
              width: buttonSize[deviceType][button[0]][width],
              textSize: buttonSize[deviceType][button[0]][text],
              buttonText: "التالي",
              onPressed: () {
                print(activeIndex);
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) =>
                        DesiredBodyShapePage(user: widget.user),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}


This is Class code that i want to store value on it

class UserFormData {
  String? name;
  int? age;
  String? gender;
  List<String>? muscle;
  String? currentBody;
  String? desiredBody;
  double? bodyHeight;
  double? bodyWeight;
  String? exercisePlace;
  List<String>? exerciseTools;
  int? activityLevel;
  int? exerciseDays;
  List<String>? selectedDays;
  String? dietSystem;
  String? dietType;
  List<String>? selectedIngredients;

  UserFormData({
    this.name,
    this.age,
    this.gender,
    this.muscle,
    this.currentBody,
    this.desiredBody,
    this.bodyHeight,
    this.bodyWeight,
    this.exercisePlace,
    this.exerciseTools,
    this.activityLevel,
    this.exerciseDays,
    this.selectedDays,
    this.dietSystem,
    this.dietType,
    this.selectedIngredients,
  });
}

This is the app view

enter image description here

I want to return value of assetImage as String and store it in class UserFormData


Solution

  • The best for bigger projects would be to use some sort of state management solution like provider, or bloc, but

    If you want to use your existing code with inheritedwidgets which can be used to access the state object of parent widgets inside of children, the quickest change would be the following:

    First add this new class:

    class _CurrentBodyShapeWrapper extends InheritedWidget {
      final _CurrentBodyShapePageState state;
    
      const _CurrentBodyShapeWrapper({
        super.key,
        required this.state,
        required super.child,
      });
    
      @override
      bool updateShouldNotify(_CurrentBodyShapeWrapper old) {
        return true;
      }
    }
    

    Then add the following method to your CurrentBodyShapePage:

      static _CurrentBodyShapePageState? of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType<_CurrentBodyShapeWrapper>()?.state;
      }
    

    And lastly change the build method of your _CurrentBodyShapePageState to include the wrapper at the top of your widget tree:

      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            automaticallyImplyLeading: false,
            elevation: 0,
            bottom: PreferredSize(
              preferredSize: Size.fromHeight(
                spaceSize[deviceType][height][space[1]],
              ),
              child: StepLineStyle(
                activeStep: 2,
                subActiveStep: 1,
              ),
            ),
          ),
          body: _CurrentBodyShapeWrapper(state: this, child: _buildBody(context)),
        );
      }
    
    
    
    
      Widget _buildBody(BuildContext context) {
        return Padding(
          padding: EdgeInsets.fromLTRB(
            0,
            spaceSize[deviceType][height][space[2]],
            0,
            spaceSize[deviceType][height][space[5]],
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                'ما هو شكل جسمك الحالي؟',
                style: TextStyle(
                    fontSize: textSize[deviceType][mainText[2]],
                    color: primaryColorStyle,
                    fontWeight: FontWeight.bold),
              ),
              BodyFormStyle(
                assetsImages: widget.user.gender == "Male"
                    ? maleAssetsImages
                    : femaleAssetsImages,
                indicatorText: 'الشكل الحالي',
                activeIndex: activeIndex,
              ),
              StartButton(
                height: buttonSize[deviceType][button[0]][height],
                width: buttonSize[deviceType][button[0]][width],
                textSize: buttonSize[deviceType][button[0]][text],
                buttonText: "التالي",
                onPressed: () {
                  print(activeIndex);
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) =>
                          DesiredBodyShapePage(user: widget.user),
                    ),
                  );
                },
              ),
            ],
          ),
        );
      }
    

    You can then use access the state in the build method of any child widget / page with the following (but i would make the State class public and remove the _ if you want to use it like this across multiple files):

    final _CurrentBodyShapePageState? state = CurrentBodyShapePage.of(context);
    

    Your use this for example inside of the callback of a button, or inside of an onChanged method.

    Your UserFormData user; should then of course also be stored inside the state object of your top most page (i assume it would be a different one than the _CurrentBodyShapePageState i used in the example.

    I hope this could help you a bit. In general you should also try to only store immutable member variables inside of the widgets themselves and store all mutable variables inside of the state objects.

    And for simple children with just one action, you can also pass a callback function parameter from the parent to the children widget on creating the widget. And the children then calls the callback method with data for the parent.