2
votes

I am new in flutter lately I was working with ListView Builder and FocusNode in flutter, I want a list of widgets only one selectable at a time, initially it works fine but when there is a long list (take it of 7 items), when I click on 1st item and go the last item in the list the 1st widget gets deactivated and rebuilds the widget which ultimately makes it not selected.

This is the list view builder

ListView.builder(
 itemCount: appliances.length,
 scrollDirection: Axis.horizontal,
 itemBuilder: (context, index) {
  return ApplicancesTile(
   appliancesData: appliances[index],
   onChanged: (value){
    setState(() {
     selected = value;
    });
   },
  );
 },
),

This is the widget

class _ApplicancesTileState extends State<ApplicancesTile> {
 FocusNode _node;
 bool _focused;
 FocusAttachment _nodeAttachment;

 @override
 void initState() {
 super.initState();
 _node = FocusNode(debugLabel: 'Button');
 _focused = _node.hasFocus;
 _node.addListener(_handleFocusChange);
 _nodeAttachment = _node.attach(context);
}

void _handleFocusChange() {
 if (_node.hasFocus != _focused) {
  setState(() {
    _focused = _node.hasFocus;
  });
 }
}

@override
Widget build(BuildContext context) {
 _nodeAttachment.reparent();
return Container(
  padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
  height: ScreenUtil().setHeight(120),
  width: ScreenUtil().setWidth(300),
  child: Material(
    color: Colors.transparent,
    child: InkWell(
      onTap: () {
        if (_focused) {
          _node.unfocus();
          widget.onChanged('null');
        } else {
          _node.requestFocus();
          widget.onChanged(widget.appliancesData.name);
        }
      },
      child: Container(
        padding: EdgeInsets.symmetric(horizontal: 10.0),
        decoration: BoxDecoration(
          color: _focused ? AppTheme.appPrimary : AppTheme.white,
          borderRadius: BorderRadius.circular(5.0),
          boxShadow: <BoxShadow>[
            BoxShadow(
                color: Colors.black.withOpacity(0.6),
                offset: Offset(0.2, 0.2),
                blurRadius: 0.9),
          ],
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(2.0),
              child: Image.asset(
                widget.appliancesData.icon,
                color: _focused ? AppTheme.white : AppTheme.nearlyBlack,
                width: ScreenUtil().setWidth(65),
              ),
            ),
            Padding(
              padding: EdgeInsets.all(2.0),
              child: Text(widget.appliancesData.name,
                  style: _focused ? selected : notSelected),
            ),
            Padding(
              padding: EdgeInsets.all(2.0),
              child: Text(
                '20 kW',
                style: _focused ? selected : notSelected,
              ),
            ),
          ],
        ),
      ),
    ),
  ),
);
1
Can you share your code snippet?Ayush Bherwani
I have edited the question and added the code snippet there @AyushBherwaniYogesh Chawla

1 Answers

2
votes

IMHO, your _focused variable is always reset to default value, while you scrolling the list.

When a child is scrolled out of view, the associated element subtree, states and render objects are destroyed. A new child at the same position in the list will be lazily recreated along with new elements, states and render objects when it is scrolled back.

You could try this: ( move the _focused state to the parent )

class SamplePage extends StatefulWidget {

    SamplePage({Key key}) : super(key: key);

    @override
    State<StatefulWidget> createState() => SamplePageState();

}

class SamplePageState extends State<SamplePage> {

    List<List<String>> items = List();

    @override
    void initState() {
        super.initState();
        items.add(["Item 00", "selected"]);
        items.add(["Item 01", ""]);
        items.add(["Item 02", ""]);
        items.add(["Item 03", ""]);
        items.add(["Item 04", ""]);
        items.add(["Item 05", ""]);
        items.add(["Item 06", ""]);
        items.add(["Item 07", ""]);
        items.add(["Item 08", ""]);
        items.add(["Item 09", ""]);
        items.add(["Item 10", ""]);
        items.add(["Item 11", ""]);
        items.add(["Item 12", ""]);
        items.add(["Item 13", ""]);
        items.add(["Item 14", ""]);
        items.add(["Item 15", ""]);
        items.add(["Item 16", ""]);
        items.add(["Item 17", ""]);
        items.add(["Item 18", ""]);
        items.add(["Item 19", ""]);
    }

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            body: Container(
                height: 600,
                child: ListView.builder(
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                        return GestureDetector(
                            behavior: HitTestBehavior.opaque,
                            child: Container(
                                height: 80,
                                color: items[index][1] == "selected" ? Colors.lightBlue : Colors.white,
                                child: Text(items[index][0]),
                            ),
                            onTap: () {
                                for (final item in items) {
                                    if (item[1] == 'selected') item[1] = '';
                                }
                                setState(() => items[index][1] = 'selected');
                            },
                        );
                    }
                ),
            ),
        );
    }

}