Search code examples
fluttersetstatemobile-developmentstatefulwidgetsinglechildscrollview

How to manage a custom widget state in SingleChildScrollView Widget


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"),
    );
  }
}


Solution

  • 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(),
    ),