Flutter - select 列表视图中只有一个项目

Flutter - select only single item in list view

在我的应用程序中,我正在生成一个 ListView 并且可以通过点击它们来突出显示项目。这很好用,我还有一个回调函数,它为我提供了刚刚选择的项目的密钥。我目前可以通过再次点击该项目来手动取消选择它,但最终会取消该功能。

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

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

我曾尝试访问磁贴 class 中的方法并使用 setter 但到目前为止都没有成功。我对 flutter 还很陌生,所以它可能是我遗漏的一些简单的东西。我以前的经验是使用 Actionscript,这个系统可以正常工作,我可以轻松访问对象(在本例中是磁贴)的方法,只要它是 public 方法即可。

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

我父class中的代码如下:

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

那么方块class如下:

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 是构成图块的对象。

在工作草案中,我有一个功能 returns 一个更新的图块列表,它应该显示选择了哪个图块(以及其他按钮的任何其他更改)。这在第一次进入屏幕时被调用。当点击图块时,它使用回调函数重绘 working_draft class 但这似乎并没有像我期望的那样重绘列表。任何进一步的指导将不胜感激。

class 是:

第一个class是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.name,
      this.currentContent,
      this.currentID,
      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"),
        outlineName: element[0],
        myContent: element[1],
        myIndex: _counter,
        onTileSelected: clearHilights,
        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,
            itemBuilder: (context, 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.outlineName,
      this.myIcon,
      this.myContent,
      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),
      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 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,
                  child: IconButton(
                      iconSize: 30,
                      icon: Icon(Icons.arrow_drop_down),
                      onPressed: () {
                        print("Move Down");
                      }),
                ),
              ],
            ),
          ),
          SizedBox(
            height: 60,
            child: Column(
              children: [
                SizedBox(
                  height: 20,
                  child: IconButton(
                      iconSize: 20,
                      icon: Icon(Icons.add_box),
                      onPressed: () {
                        print("Add another");
                      }),
                ),
                SizedBox(
                  height: 10,
                ),
                SizedBox(
                  height: 20,
                  child: IconButton(
                      iconSize: 20,
                      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;
            });
          },
        );
      },
    );
  }
}