Home > Net >  How to manage a custom widget state in SingleChildScrollView Widget
How to manage a custom widget state in SingleChildScrollView Widget

Time:11-15

I'm trying to design this view.

enter image description here

I already have the basic design of the cards, but i would like to know how to change the card's background color, the card's border color and add the little green square according to the width size of the current card when the user click one of them. It's important to know that only one card can be painted in green when the user clicked it.

Here is my code:

CategoryCardModel

class CategoryCardModel {
  final String? categoryCardModelName;

  CategoryCardModel(this.categoryCardModelName);
}

CategoryCard

import 'dart:ffi';

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class CategoryCard extends StatelessWidget {
  final String? categoryCardName;
  final Function()? wasPressed;

  const CategoryCard({
    super.key,
    required this.categoryCardName,
    this.wasPressed,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: wasPressed,
      child: Card(
        shape: RoundedRectangleBorder(
          side: const BorderSide(
            color: Color.fromRGBO(212, 213, 215, 100),
            width: 3,
          ),
          borderRadius: BorderRadius.circular(20.0),
        ),
        child: Container(
          decoration: BoxDecoration(
              color: const Color.fromRGBO(242, 243, 243, 100),
              borderRadius: BorderRadius.circular(20.0)),
          padding: const EdgeInsets.all(10),
          child: Text(
            categoryCardName ?? 'Todas',
            style: const TextStyle(
                fontSize: 25,
                fontWeight: FontWeight.bold,
                color: Color.fromRGBO(91, 94, 99, 100)),
          ),
        ),
      ),
    );
  }
}

MyHomePage

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'category_card.dart';
import 'category_card_model.dart';

class MyHomePage extends StatefulWidget {
  MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // List of models
  final categoryCardModelList = <CategoryCardModel>[
    CategoryCardModel("Todas"),
    CategoryCardModel("Smartphones"),
    CategoryCardModel("Accesorios para celular"),
    CategoryCardModel("TV")
  ];

  List<CategoryCardModel>? _categoryCardModelListOf;

  @override
  void initState() {
    super.initState();
    setState(() {
      _categoryCardModelListOf = List.of(categoryCardModelList);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: _categoryCardModelListOf!
                    .map<Widget>((categoryCardModel) => CategoryCard(
                        wasPressed: () {
                          print("Hello World");
                          setState(() {});
                        },
                        categoryCardName:
                            categoryCardModel.categoryCardModelName))
                    .toList())));
  }
}

main

import 'package:flutter/material.dart';

import 'my_home_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: "Caregory Cards"),
    );
  }
}

CodePudding user response:

Selected is needed for Card

class CategoryCard extends StatelessWidget {
  final String? categoryCardName;
  final Function()? wasPressed;
  final bool isActive;

  const CategoryCard(
      {super.key,
      required this.categoryCardName,
      this.wasPressed,
      this.isActive = false});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: wasPressed,
      child: Card(
        shape: const StadiumBorder(),
        child: Container(
          decoration: BoxDecoration(
              color: (isActive ? Colors.green : Colors.grey).withOpacity(.1),
              borderRadius: BorderRadius.circular(24.0),
              border: Border.all(
                  width: 2, color: isActive ? Colors.green : Colors.grey)),
          padding: const EdgeInsets.all(10),
          child: Text(
            categoryCardName ?? 'Todas',
            style: const TextStyle(
                fontSize: 25,
                fontWeight: FontWeight.bold,
                color: Color.fromRGBO(91, 94, 99, 100)),
          ),
        ),
      ),
    );
  }
}

Create a state variable for selected model CategoryCardModel? activeTab; And use

children: _categoryCardModelListOf!
    .map<Widget>((categoryCardModel) => CategoryCard(
        isActive: activeTab == categoryCardModel,
        wasPressed: () {
          activeTab = categoryCardModel;
          setState(() {});
        },
        categoryCardName: categoryCardModel.categoryCardModelName))
    .toList(),
),

CodePudding user response:

Update your CategoryCard class like this, you may need to change the color according to your desire :

class CategoryCard extends StatelessWidget {
  final String? categoryCardName;
  final Function()? wasPressed;
  final bool isSelected;

  const CategoryCard({
    super.key,
    required this.categoryCardName,
    this.wasPressed,
    this.isSelected = false,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: wasPressed,
      child: Card(
        shape: RoundedRectangleBorder(
          side: BorderSide(
            color: isSelected ? Colors.green : Color.fromRGBO(212, 213, 215, 100),
            width: 3,
          ),
          borderRadius: BorderRadius.circular(20.0),
        ),
        child: Container(
          decoration: BoxDecoration(
            color: isSelected ? Colors.greenAccent : const Color.fromRGBO(242, 243, 243, 100),
            borderRadius: BorderRadius.circular(20.0),
          ),
          padding: const EdgeInsets.all(10),
          child: Text(
            categoryCardName ?? 'Todas',
            style: const TextStyle(fontSize: 25, fontWeight: FontWeight.bold, color: Color.fromRGBO(91, 94, 99, 100)),
          ),
        ),
      ),
    );
  }
}

And then change your _MyHomePageState class to this :

class _MyHomePageState extends State<MyHomePage> {
  // List of models
  final categoryCardModelList = <CategoryCardModel>[
    CategoryCardModel("Todas"),
    CategoryCardModel("Smartphones"),
    CategoryCardModel("Accesorios para celular"),
    CategoryCardModel("TV")
  ];

  List<CategoryCardModel>? _categoryCardModelListOf;
  CategoryCardModel? _selectedCardModel;

  @override
  void initState() {
    super.initState();
    setState(() {
      _categoryCardModelListOf = List.of(categoryCardModelList);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: _categoryCardModelListOf!
              .map<Widget>((categoryCardModel) => CategoryCard(
            wasPressed: () {
              print("Hello World");
              setState(() {
                _selectedCardModel = categoryCardModel;
              });
            },
            categoryCardName: categoryCardModel.categoryCardModelName,
            isSelected: _selectedCardModel == categoryCardModel,
          ))
              .toList(),
        ),
      ),
    );
  }
}

CodePudding user response:

Use above two answers for highlighting selected option...and here is what missing...

The underline below selected tab...

for that update your category card as below,

as u have mentioned underline width must be in size of tab width,

I have used ** IntrinsicWidth**


class CategoryCard extends StatelessWidget {
  final String? categoryCardName;
  final Function()? wasPressed;
  final bool? isselected;

  const CategoryCard({
    super.key,
    required this.categoryCardName,
    this.wasPressed,
    this.isselected=false
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: wasPressed,
      child: IntrinsicWidth(
        child: Padding(
          padding: EdgeInsets.symmetric(horizontal: 10),
          child: Column(children: [
            Card(
              shape: RoundedRectangleBorder(
                side:  BorderSide(
                  color:isselected==true?Colors.red: Color.fromRGBO(212, 213, 215, 100),
                  width: 3,
                ),
                borderRadius: BorderRadius.circular(20.0),
              ),
              child: Container(
                decoration: BoxDecoration(
                    color: const Color.fromRGBO(242, 243, 243, 100),
                    borderRadius: BorderRadius.circular(20.0)),
                padding: const EdgeInsets.all(10),
                child: Text(
                  categoryCardName ?? 'Todas',
                  style: const TextStyle(
                      fontSize: 25,
                      fontWeight: FontWeight.bold,
                      color: Color.fromRGBO(91, 94, 99, 100)),
                ),
              ),
            ),
            if(isselected==true)
              Padding(
                padding: EdgeInsets.symmetric(horizontal: 20),
                child: Container(
                      color: Colors.red[200],
                height: 5,
            ),
              ),
          ],),
        ),
      ),
    );
  }
}
  • Related