Flutter-在列表视图中仅选择单个项目

问题描述

在我的应用程序中,我正在生成一个ListView,通过点按项目可以突出显示它们。效果很好,我还有一个回调函数,可为我提供所选项目的键。目前,我可以通过再次点击来手动取消选择该项目,但最终将使该功能失效。

我的问题是我希望一次仅选择一项。为了创建列表,我目前以列表的形式获取一些初始内容生成切片并将其添加到另一个列表中。然后,我使用该列表创建ListView。我的计划是从新选择中进行回调,遍历图块列表并取消选择它们,然后突出显示新选择的图块并执行其他功能。我尝试了各种方法来告诉每个图块取消选择自己,但是还没有找到解决每个图块的方法。目前,我收到错误消息:

类'OutlineTile'没有实例方法'deselect'。 接收方:“ OutlineTile”的实例 尝试调用deselect()

我试图访问tile类中的方法并使用setter,但到目前为止都没有奏效。我很陌生,所以可能缺少一些简单的东西。我以前的经验是在Actionscript上工作​​的,该系统可以很好地运行,并且只要它是公共方法,我就可以轻松地访问对象的方法(在本例中为tile)。

我很高兴有另一种方法来取消选择旧项目或找到一种方法来访问图块中的方法面临的挑战是,在不点击的情况下,让磁贴显示不突出显示,而是在点击其他磁贴时显示

父类中的代码如下:

class WorkingDraft extends StatefulWidget {
  final String startType;
  final String name;
  final String currentContent;
  final String currentID;
  final List startContent;

  WorkingDraft(
      {this.startType,this.name,this.currentContent,this.currentID,this.startContent});

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

class _WorkingDraftState extends State<WorkingDraft> {
  final _formKey = GlobalKey<FormState>();
  final myController = TextEditingController();
  //String _startType;
  String _currentContent = "";
  String _name = "Draft";
  List _startContent = [];
  List _outLineTiles = [];
  int _counter = 0;

  @override
  void dispose() {
    // Clean up the controller when the widget is disposed.
    myController.dispose();
    super.dispose();
  }

  void initState() {
    super.initState();

    _currentContent = widget.currentContent;
    _name = widget.name;
    _startContent = widget.startContent;
    _counter = 0;

    _startContent.forEach((element) {
      _outLineTiles.add(OutlineTile(
        key: Key("myKey$_counter"),outlineName: element[0],myContent: element[1],onTileSelected: clearHilights,));
      _counter++;
    });
  }

  dynamic clearHilights(Key myKey) {
    _outLineTiles.forEach((element) {
      element.deselect();  // this throws an error Class 'OutlineTile' has no instance method 'deselect'.
      Key _foundKey = element.key;
      print("Element Key $_foundKey");
    });
  }
.......

并进一步在小部件构建支架中:

      child: ListView.builder(
        itemCount: _startContent.length,itemBuilder: (context,index) {
          return _outLineTiles[index];
        },),

然后tile类如下:

class OutlineTile extends StatefulWidget {
  final Key key;
  final String outlineName;
  final Icon myIcon;
  final String myContent;
  final Function(Key) onTileSelected;

  OutlineTile(
      {this.key,this.outlineName,this.myIcon,this.myContent,this.onTileSelected});

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

class _OutlineTileState extends State<OutlineTile> {
  Color color;
  Key _myKey;

  @override
  void initState() {
    super.initState();

    color = Colors.transparent;
  }

  bool _isSelected = false;
  set isSelected(bool value) {
    _isSelected = value;
    print("set is selected to $_isSelected");
  }

  void changeSelection() {
    setState(() {
      _myKey = widget.key;
      _isSelected = !_isSelected;
      if (_isSelected) {
        color = Colors.lightBlueAccent;
      } else {
        color = Colors.transparent;
      }
    });
  }

  void deselect() {
    setState(() {
      isSelected = false;
      color = Colors.transparent;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 4.0),child: Row(
        children: [
          Card(
            elevation: 10,margin: EdgeInsets.fromLTRB(10.0,6.0,5.0,0.0),child: SizedBox(
              width: 180,child: Container(
                color: color,child: ListTile(
                  title: Text(widget.outlineName),onTap: () {
                    if (widget.outlineName == "heading") {
                      Text("Called heading");
                    } else (widget.outlineName == "Paragraph") {
                      Text("Called Paragraph");
                    widget.onTileSelected(_myKey);
                    changeSelection();
                  },........ 

感谢您的帮助。

可从此处构建完整项目的修订的代码示例和说明:

根据phimath的建议,我创建了项目相关部分的完整可构建示例。

问题是我的列表视图中的图块更复杂,包含多个元素,其中许多元素本身就是按钮,因此,虽然phimath的解决方案适用于简单的文本图块,但我无法在我自己的项目中使用它。我的方法是尝试从根本上实现与Phimath相同的功能,但是当我包含这些更复杂的图块时,它将无法正常工作。

此示例项目由三个文件组成。 main.dart只是调用项目,并以我的主项目的方式传递一些虚拟数据。 working_draft.dart,这是此问题的核心。还有outline_tile.dart,它是形成图块的对象。

在工作草案中,我有一个函数可以返回更新的磁贴列表,其中应显示所选的磁贴(以及其他按钮的其他更改)。第一次进入屏幕时会调用方法。轻触图块时,它使用回调函数来重绘working_draft类,但这似乎并没有像我期望的那样重绘列表。任何进一步的指导将不胜感激。

这些类是:

第一堂课是main.dart:

import 'package:Flutter/material.dart';
import 'package:listexp/working_draft.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: WorkingDraft(
      startType: "Basic",name: "Draft",currentID: "anID",startContent: [
        ["heading","New heading"],["Paragraph","New Text"],["Image","placeholder"],["Signature","placeholder"]
      ],));
  }
}

一个文件是working_draft.dart:

import 'package:Flutter/material.dart';
import 'package:listexp/outline_tile.dart';

class WorkingDraft extends StatefulWidget {
  final String startType;
  final String name;
  final String currentContent;
  final String currentID;
  final List startContent;
  final int selectedindex;

  WorkingDraft(
      {this.startType,this.startContent,this.selectedindex});

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

class _WorkingDraftState extends State<WorkingDraft> {
  int selectedindex;
  String _currentContent = "";
  String _name = "Draft";
  List _startContent = [];
  var _outLineTiles = [];
  int _counter = 0;
  int _selectedindex;
  bool _isSelected;

  dynamic clearHilights(int currentIndex) {
    setState(() {
      _selectedindex = currentIndex;
    });
  }

  updatedTiles() {
    if (_selectedindex == null) {
      _selectedindex = 0;
    }

    _currentContent = widget.currentContent;
    _name = widget.name;
    _startContent = widget.startContent;
    _counter = 0;
    _outLineTiles = [];
    _startContent.forEach((element) {
      _isSelected = _selectedindex == _counter ? true : false;
      _outLineTiles.add(OutlineTile(
        key: Key("myKey$_counter"),myIndex: _counter,isSelected: _isSelected,));
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    updatedTiles();
    return Scaffold(
        body: Center(
      child: Column(children: [
        SizedBox(height: 100),Text("Outline",style: new TextStyle(fontSize: 15)),Container(
          height: 215,width: 300,decoration: Boxdecoration(
            border: Border.all(
              color: Colors.lightGreenAccent,width: 2,borderRadius: BorderRadius.circular(2),child: ListView.builder(
            itemCount: _startContent.length,index) {
              return _outLineTiles[index];
            },]),));
  }
}

最后是outline_tile.dart

import 'package:Flutter/material.dart';

class OutlineTile extends StatefulWidget {
  final Key key;
  final String outlineName;
  final Icon myIcon;
  final String myContent;
  final int myIndex;
  final Function(int) onTileSelected;
  final bool isSelected;

  OutlineTile(
      {this.key,this.myIndex,this.onTileSelected,this.isSelected});

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

class _OutlineTileState extends State<OutlineTile> {
  Color color;
  // Key _myKey;
  bool _isSelected;

  @override
  void initState() {
    super.initState();

    _isSelected = widget.isSelected;

    if (_isSelected == true) {
      color = Colors.lightBlueAccent;
    } else {
      color = Colors.transparent;
    }
  }

  void deselect() {
    setState(() {
      _isSelected = widget.isSelected;

      if (_isSelected == true) {
        color = Colors.lightBlueAccent;
      } else {
        color = Colors.transparent;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 4.0),onTap: () {
                    if (widget.outlineName == "heading") {
                      Text("Called heading");
                    } else if (widget.outlineName == "Paragraph") {
                      Text("Called Paragraph");
                    } else if (widget.outlineName == "Signature") {
                      Text("Called Signature");
                    } else {
                      Text("Called Image");
                    }
                    var _myIndex = widget.myIndex;

                    widget.onTileSelected(_myIndex);
                    deselect();
                  },SizedBox(
            height: 60,child: Column(
              children: [
                SizedBox(
                  height: 20,child: IconButton(
                      iconSize: 30,icon: Icon(Icons.arrow_drop_up),onpressed: () {
                        print("Move Up");
                      }),SizedBox(height: 5),SizedBox(
                  height: 20,icon: Icon(Icons.arrow_drop_down),onpressed: () {
                        print("Move Down");
                      }),],child: IconButton(
                      iconSize: 20,icon: Icon(Icons.add_Box),onpressed: () {
                        print("Add another");
                      }),SizedBox(
                  height: 10,icon: Icon(Icons.delete),onpressed: () {
                        print("Delete");
                      }),);
  }
}

再次感谢

解决方法

无需手动取消选择图块,只需跟踪当前选择的图块即可。

我为您提供了一个简单的例子。当我们单击一个图块时,只需将所选索引设置为我们单击的索引,然后每个图块都会查看该索引以查看其当前选定的图块。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter',theme: ThemeData(
        primarySwatch: Colors.blue,),home: Scaffold(body: Home()),);
  }
}

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  int selectedIndex;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 10,itemBuilder: (context,index) {
        return ListTile(
          title: Text('Item: $index'),tileColor: selectedIndex == index ? Colors.blue : null,onTap: () {
            setState(() {
              selectedIndex = index;
            });
          },);
      },);
  }
}